diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/NetworkGUI.cs b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/NetworkGUI.cs index 180392a..fbd763f 100644 --- a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/NetworkGUI.cs +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/NetworkGUI.cs @@ -21,14 +21,6 @@ namespace VelNet public Dropdown microphones; private DissonanceComms comms; - public void HandleSend() - { - if (sendInput.text != "") - { - VelNetManager.SendToRoom(Encoding.UTF8.GetBytes(sendInput.text)); - } - } - public void HandleLogin() { if (userInput.text != "") diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs index 5c605ac..9c09aef 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs @@ -62,7 +62,7 @@ namespace VelNet } else { - owner.SendMessage(this, index.ToString(), message, reliable); + VelNetPlayer.SendMessage(this, (byte)index, message, reliable); } } @@ -76,7 +76,7 @@ namespace VelNet // send the message and an identifier for which component it belongs to int index = syncedComponents.IndexOf(component); - owner.SendGroupMessage(this, group, index.ToString(), message, reliable); + VelNetPlayer.SendGroupMessage(this, group, (byte)index, message, reliable); } public void ReceiveBytes(string identifier, byte[] message) @@ -128,6 +128,7 @@ namespace VelNet foreach (NetworkComponent c in comps) { c.networkObject = t; + PrefabUtility.RecordPrefabInstancePropertyModifications(c); } PrefabUtility.RecordPrefabInstancePropertyModifications(t); } diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/BinaryWriterExtensions.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/BinaryWriterExtensions.cs index a605837..309a239 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/BinaryWriterExtensions.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/BinaryWriterExtensions.cs @@ -65,14 +65,20 @@ namespace VelNet #endregion - public static bool SameAs(this byte[] bytes, byte[] otherBytes) + public static bool BytesSame(byte[] b1, byte[] b2) { - if (bytes.Length != otherBytes.Length) + if (b1 == null && b2 != null) return false; // only one null + if (b1 != null && b2 == null) return false; // only one null + if (b1 == null) return true; // both null + + // length doesn't match + if (b1.Length != b2.Length) { return false; } - return !bytes.Where((t, i) => t != otherBytes[i]).Any(); + // check if any bytes are different + return !b1.Where((t, i) => t != b2[i]).Any(); } /// diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs index f6fc434..a645854 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs @@ -34,7 +34,7 @@ namespace VelNet byte[] newBytes = SendState(); if (hybridOnChangeCompression) { - if (Time.timeAsDouble - lastSendTime > slowSendInterval || !lastSentBytes.SameAs(newBytes)) + if (Time.timeAsDouble - lastSendTime > slowSendInterval || !BinaryWriterExtensions.BytesSame(lastSentBytes, newBytes)) { SendBytes(newBytes); } diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObjectStream.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObjectStream.cs index d3ac592..7623122 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObjectStream.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObjectStream.cs @@ -40,7 +40,7 @@ namespace VelNet byte[] newBytes = mem.ToArray(); if (hybridOnChangeCompression) { - if (Time.timeAsDouble - lastSendTime > slowSendInterval || !lastSentBytes.SameAs(newBytes)) + if (Time.timeAsDouble - lastSendTime > slowSendInterval || !BinaryWriterExtensions.BytesSame(lastSentBytes, newBytes)) { SendBytes(newBytes); } diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs index c166404..670e2c3 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs @@ -27,6 +27,15 @@ namespace VelNet MESSAGE_SETGROUP = 6 }; + public enum MessageType + { + ObjectSync, + TakeOwnership, + Instantiate, + Destroy, + DeleteSceneObjects + } + public string host; public int port; @@ -455,6 +464,8 @@ namespace VelNet private void OnApplicationQuit() { socketConnection?.Close(); + clientReceiveThreadUDP?.Abort(); + clientReceiveThread?.Abort(); } /// @@ -472,11 +483,13 @@ namespace VelNet Debug.Log("On client connect exception " + e); } } - - - /// - /// Runs in background clientReceiveThread; Listens for incoming data. + + /// + /// Reads N bytes /// + /// + /// + /// private static byte[] ReadExact(Stream stream, int N) { byte[] toReturn = new byte[N]; @@ -505,6 +518,7 @@ namespace VelNet socketConnection = new TcpClient(host, port); socketConnection.NoDelay = true; NetworkStream stream = socketConnection.GetStream(); + using BinaryReader reader = new BinaryReader(stream); //now we are connected, so add a message to the queue AddMessage(new ConnectedMessage()); //Join("MyRoom"); @@ -515,62 +529,74 @@ namespace VelNet { // Get a stream object for reading - //read a byte - byte type = (byte)stream.ReadByte(); + byte type = reader.ReadByte(); - if (type == 0) //login + switch (type) { - LoginMessage m = new LoginMessage(); - m.userId = GetIntFromBytes(ReadExact(stream, 4)); //not really the sender... - AddMessage(m); - } - else if (type == 1) //rooms - { - RoomsMessage m = new RoomsMessage(); - m.rooms = new List(); - int N = GetIntFromBytes(ReadExact(stream, 4)); //the size of the payload - byte[] utf8data = ReadExact(stream, N); - string roomMessage = Encoding.UTF8.GetString(utf8data); - - - string[] sections = roomMessage.Split(','); - foreach (string s in sections) + //login + case 0: { - string[] pieces = s.Split(':'); - if (pieces.Length == 2) - { - ListedRoom lr = new ListedRoom(); - lr.name = pieces[0]; - lr.numUsers = int.Parse(pieces[1]); - m.rooms.Add(lr); - } + LoginMessage m = new LoginMessage(); + m.userId = reader.ReadInt32(); //not really the sender... + AddMessage(m); + break; } + //rooms + case 1: + { + RoomsMessage m = new RoomsMessage(); + m.rooms = new List(); + int N = reader.ReadInt32(); //the size of the payload + byte[] utf8data = reader.ReadBytes(N); + string roomMessage = Encoding.UTF8.GetString(utf8data); - AddMessage(m); - } - else if (type == 2) //joined - { - JoinMessage m = new JoinMessage(); - m.userId = GetIntFromBytes(ReadExact(stream, 4)); - int N = stream.ReadByte(); - byte[] utf8data = ReadExact(stream, N); //the room name, encoded as utf-8 - m.room = Encoding.UTF8.GetString(utf8data); - AddMessage(m); - } - else if (type == 3) //data - { - DataMessage m = new DataMessage(); - m.senderId = GetIntFromBytes(ReadExact(stream, 4)); - int N = GetIntFromBytes(ReadExact(stream, 4)); //the size of the payload - m.data = ReadExact(stream, N); //the message - AddMessage(m); - } - else if (type == 4) //new master - { - ChangeMasterMessage m = new ChangeMasterMessage(); - m.masterId = GetIntFromBytes(ReadExact(stream, 4)); //sender is the new master - AddMessage(m); + + string[] sections = roomMessage.Split(','); + foreach (string s in sections) + { + string[] pieces = s.Split(':'); + if (pieces.Length == 2) + { + ListedRoom lr = new ListedRoom(); + lr.name = pieces[0]; + lr.numUsers = int.Parse(pieces[1]); + m.rooms.Add(lr); + } + } + + AddMessage(m); + break; + } + //joined + case 2: + { + JoinMessage m = new JoinMessage(); + m.userId = reader.ReadInt32(); + int N = reader.ReadByte(); + byte[] utf8data = reader.ReadBytes(N); //the room name, encoded as utf-8 + m.room = Encoding.UTF8.GetString(utf8data); + AddMessage(m); + break; + } + //data + case 3: + { + DataMessage m = new DataMessage(); + m.senderId = reader.ReadInt32(); + int N = reader.ReadInt32(); //the size of the payload + m.data = reader.ReadBytes(N); //the message + AddMessage(m); + break; + } + //new master + case 4: + { + ChangeMasterMessage m = new ChangeMasterMessage(); + m.masterId = reader.ReadInt32(); //sender is the new master + AddMessage(m); + break; + } } } } @@ -659,7 +685,7 @@ namespace VelNet } /// - /// Send message to server using socket connection. + /// Send message to server using socket connection. /// private static void SendTcpMessage(byte[] message) //we can assume that this message is already formatted, so we just send it { @@ -684,15 +710,14 @@ namespace VelNet } } - /// - /// Connects to the server with a username - /// - /// public static byte[] get_be_bytes(int n) { return BitConverter.GetBytes(n).Reverse().ToArray(); } + /// + /// Connects to the server with a username + /// public static void Login(string username, string password) { MemoryStream stream = new MemoryStream(); @@ -742,7 +767,7 @@ namespace VelNet } } - public static void SendToRoom(byte[] message, bool include_self = false, bool reliable = true, bool ordered = false) + internal static void SendToRoom(byte[] message, bool include_self = false, bool reliable = true, bool ordered = false) { byte sendType = (byte)MessageSendType.MESSAGE_OTHERS; if (include_self && ordered) sendType = (byte)MessageSendType.MESSAGE_ALL_ORDERED; @@ -752,12 +777,12 @@ namespace VelNet if (reliable) { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); + MemoryStream mem = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(mem); writer.Write(sendType); writer.Write(get_be_bytes(message.Length)); writer.Write(message); - SendTcpMessage(stream.ToArray()); + SendTcpMessage(mem.ToArray()); } else { @@ -770,7 +795,7 @@ namespace VelNet } - public static void SendToGroup(string group, byte[] message, bool reliable = true) + internal static void SendToGroup(string group, byte[] message, bool reliable = true) { byte[] utf8bytes = Encoding.UTF8.GetBytes(group); if (reliable) @@ -845,8 +870,14 @@ namespace VelNet newObject.owner = localPlayer; instance.objects.Add(newObject.networkId, newObject); + // only sent to others, as I already instantiated this. Nice that it happens immediately. - SendToRoom(Encoding.UTF8.GetBytes("7," + newObject.networkId + "," + prefabName), false, true); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.Instantiate); + writer.Write(newObject.networkId); + writer.Write(prefabName); + SendToRoom(mem.ToArray(), include_self:false, reliable:true); return newObject; } @@ -917,8 +948,13 @@ namespace VelNet // immediately successful instance.objects[networkId].owner = LocalPlayer; - // must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. - SendToRoom(Encoding.UTF8.GetBytes("6," + networkId)); + // must be ordered, so that ownership transfers are not confused. + // Also sent to all players, so that multiple simultaneous requests will result in the same outcome. + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.TakeOwnership); + writer.Write(networkId); + SendToRoom(mem.ToArray(), false, true); return true; } diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs index 56bb8ff..f4fb524 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System; +using System.IO; using System.Text; namespace VelNet @@ -40,7 +41,12 @@ namespace VelNet { if (kvp.Value.owner == this && kvp.Value.prefabName != "") { - VelNetManager.SendToRoom(Encoding.UTF8.GetBytes("7," + kvp.Value.networkId + "," + kvp.Value.prefabName),false,true); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.Instantiate); + writer.Write(kvp.Value.networkId); + writer.Write(kvp.Value.prefabName); + VelNetManager.SendToRoom(mem.ToArray(), false, true); } } @@ -54,27 +60,39 @@ namespace VelNet /// /// These are generally things that come from the "owner" and should be enacted locally, where appropriate + /// + /// Overall message encoding: + /// uint16: numMessages + /// for m in numMessages + /// int32: message size (including message type) + /// byte: message type + /// byte[]: message /// public void HandleMessage(VelNetManager.DataMessage m) { - //for now, we can just convert to text...because + using MemoryStream mem = new MemoryStream(m.data); + using BinaryReader reader = new BinaryReader(mem); - string text = Encoding.UTF8.GetString(m.data); - - //types of messages - string[] messages = text.Split(';'); //messages are split by ; - foreach (string s in messages) + ushort numMessages = reader.ReadUInt16(); + + for (int i = 0; i < numMessages; i++) { //individual message parameters separated by comma - string[] sections = s.Split(','); + int messageLength = reader.ReadInt32(); + VelNetManager.MessageType messageType = (VelNetManager.MessageType)reader.ReadByte(); + byte[] message = reader.ReadBytes(messageLength-1); + + // make a separate reader to prevent malformed messages from messing us up + using MemoryStream messageMem = new MemoryStream(message); + using BinaryReader messageReader = new BinaryReader(messageMem); - switch (sections[0]) + switch (messageType) { - case "5": // sync update for an object I may own + case VelNetManager.MessageType.ObjectSync: // sync update for an object I may own { - string objectKey = sections[1]; - string identifier = sections[2]; - string syncMessage = sections[3]; + string objectKey = messageReader.ReadString(); + string identifier = messageReader.ReadString(); + string syncMessage = messageReader.ReadString(); byte[] messageBytes = Convert.FromBase64String(syncMessage); if (manager.objects.ContainsKey(objectKey)) { @@ -86,9 +104,9 @@ namespace VelNet break; } - case "6": // I'm trying to take ownership of an object + case VelNetManager.MessageType.TakeOwnership: // I'm trying to take ownership of an object { - string networkId = sections[1]; + string networkId = messageReader.ReadString(); if (manager.objects.ContainsKey(networkId)) { @@ -97,10 +115,10 @@ namespace VelNet break; } - case "7": // I'm trying to instantiate an object + case VelNetManager.MessageType.Instantiate: // I'm trying to instantiate an object { - string networkId = sections[1]; - string prefabName = sections[2]; + string networkId = messageReader.ReadString(); + string prefabName = messageReader.ReadString(); if (manager.objects.ContainsKey(networkId)) { break; //we already have this one, ignore @@ -110,22 +128,25 @@ namespace VelNet break; } - case "8": // I'm trying to destroy a gameobject I own + case VelNetManager.MessageType.Destroy: // I'm trying to destroy a gameobject I own { - string networkId = sections[1]; + string networkId = messageReader.ReadString(); VelNetManager.NetworkDestroy(networkId); break; } - case "9": //deleted scene objects + case VelNetManager.MessageType.DeleteSceneObjects: //deleted scene objects { - for (int k = 1; k < sections.Length; k++) + int len = messageReader.ReadInt32(); + for (int k = 1; k < len; k++) { - VelNetManager.NetworkDestroy(sections[k]); + VelNetManager.NetworkDestroy(messageReader.ReadString()); } break; } + default: + throw new ArgumentOutOfRangeException(); } } } @@ -137,22 +158,39 @@ namespace VelNet //FindObjectsOfType(); } - public void SendGroupMessage(NetworkObject obj, string group, string identifier, byte[] data, bool reliable = true) + public static void SendGroupMessage(NetworkObject obj, string group, byte componentIdx, byte[] data, bool reliable = true) { - string message = "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data); - VelNetManager.SendToGroup(group, Encoding.UTF8.GetBytes(message), reliable); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.ObjectSync); + writer.Write(obj.networkId); + writer.Write(componentIdx); + writer.Write(data); + VelNetManager.SendToGroup(group, mem.ToArray(), reliable); } - public void SendMessage(NetworkObject obj, string identifier, byte[] data, bool reliable = true) + public static void SendMessage(NetworkObject obj, byte componentIdx, byte[] data, bool reliable = true) { - string message = "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data); - VelNetManager.SendToRoom(Encoding.UTF8.GetBytes(message), false, reliable); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.ObjectSync); + writer.Write(obj.networkId); + writer.Write(componentIdx); + writer.Write(data); + VelNetManager.SendToRoom(mem.ToArray(), false, reliable); } public void SendSceneUpdate() { - string message = "9," + string.Join(",", manager.deletedSceneObjects); - VelNetManager.SendToRoom( Encoding.UTF8.GetBytes(message)); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.DeleteSceneObjects); + writer.Write(manager.deletedSceneObjects.Count); + foreach (string o in manager.deletedSceneObjects) + { + writer.Write(o); + } + VelNetManager.SendToRoom(mem.ToArray()); } [Obsolete("Use VelNetManager.NetworkDestroy() instead.")] @@ -162,7 +200,12 @@ namespace VelNet if (!manager.objects.ContainsKey(networkId) || manager.objects[networkId].owner != this || !isLocal) return; // send to all, which will make me delete as well - VelNetManager.SendToRoom(Encoding.UTF8.GetBytes("8," + networkId), true, true); + + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.Destroy); + writer.Write(networkId); + VelNetManager.SendToRoom(mem.ToArray(), true, true); } /// True if successful, False if failed to transfer ownership @@ -178,8 +221,13 @@ namespace VelNet // immediately successful manager.objects[networkId].owner = this; - // must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. - VelNetManager.SendToRoom(Encoding.UTF8.GetBytes("6," + networkId),true,true); + // must be ordered, so that ownership transfers are not confused. + // Also sent to all players, so that multiple simultaneous requests will result in the same outcome. + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.TakeOwnership); + writer.Write(networkId); + VelNetManager.SendToRoom(mem.ToArray(), true, true, ordered: true); return true; }