moved message encoding to binary instead of string

BinaryServer
Anton Franzluebbers 2022-01-25 21:28:35 -05:00
parent 7d110bb8ad
commit 94333a389d
7 changed files with 199 additions and 116 deletions

View File

@ -21,14 +21,6 @@ namespace VelNet
public Dropdown microphones; public Dropdown microphones;
private DissonanceComms comms; private DissonanceComms comms;
public void HandleSend()
{
if (sendInput.text != "")
{
VelNetManager.SendToRoom(Encoding.UTF8.GetBytes(sendInput.text));
}
}
public void HandleLogin() public void HandleLogin()
{ {
if (userInput.text != "") if (userInput.text != "")

View File

@ -62,7 +62,7 @@ namespace VelNet
} }
else 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 // send the message and an identifier for which component it belongs to
int index = syncedComponents.IndexOf(component); 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) public void ReceiveBytes(string identifier, byte[] message)
@ -128,6 +128,7 @@ namespace VelNet
foreach (NetworkComponent c in comps) foreach (NetworkComponent c in comps)
{ {
c.networkObject = t; c.networkObject = t;
PrefabUtility.RecordPrefabInstancePropertyModifications(c);
} }
PrefabUtility.RecordPrefabInstancePropertyModifications(t); PrefabUtility.RecordPrefabInstancePropertyModifications(t);
} }

View File

@ -65,14 +65,20 @@ namespace VelNet
#endregion #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 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();
} }
/// <summary> /// <summary>

View File

@ -34,7 +34,7 @@ namespace VelNet
byte[] newBytes = SendState(); byte[] newBytes = SendState();
if (hybridOnChangeCompression) if (hybridOnChangeCompression)
{ {
if (Time.timeAsDouble - lastSendTime > slowSendInterval || !lastSentBytes.SameAs(newBytes)) if (Time.timeAsDouble - lastSendTime > slowSendInterval || !BinaryWriterExtensions.BytesSame(lastSentBytes, newBytes))
{ {
SendBytes(newBytes); SendBytes(newBytes);
} }

View File

@ -40,7 +40,7 @@ namespace VelNet
byte[] newBytes = mem.ToArray(); byte[] newBytes = mem.ToArray();
if (hybridOnChangeCompression) if (hybridOnChangeCompression)
{ {
if (Time.timeAsDouble - lastSendTime > slowSendInterval || !lastSentBytes.SameAs(newBytes)) if (Time.timeAsDouble - lastSendTime > slowSendInterval || !BinaryWriterExtensions.BytesSame(lastSentBytes, newBytes))
{ {
SendBytes(newBytes); SendBytes(newBytes);
} }

View File

@ -27,6 +27,15 @@ namespace VelNet
MESSAGE_SETGROUP = 6 MESSAGE_SETGROUP = 6
}; };
public enum MessageType
{
ObjectSync,
TakeOwnership,
Instantiate,
Destroy,
DeleteSceneObjects
}
public string host; public string host;
public int port; public int port;
@ -455,6 +464,8 @@ namespace VelNet
private void OnApplicationQuit() private void OnApplicationQuit()
{ {
socketConnection?.Close(); socketConnection?.Close();
clientReceiveThreadUDP?.Abort();
clientReceiveThread?.Abort();
} }
/// <summary> /// <summary>
@ -473,10 +484,12 @@ namespace VelNet
} }
} }
/// <summary> /// <summary>
/// Runs in background clientReceiveThread; Listens for incoming data. /// Reads N bytes
/// </summary> /// </summary>
/// <param name="stream"></param>
/// <param name="N"></param>
/// <returns></returns>
private static byte[] ReadExact(Stream stream, int N) private static byte[] ReadExact(Stream stream, int N)
{ {
byte[] toReturn = new byte[N]; byte[] toReturn = new byte[N];
@ -505,6 +518,7 @@ namespace VelNet
socketConnection = new TcpClient(host, port); socketConnection = new TcpClient(host, port);
socketConnection.NoDelay = true; socketConnection.NoDelay = true;
NetworkStream stream = socketConnection.GetStream(); NetworkStream stream = socketConnection.GetStream();
using BinaryReader reader = new BinaryReader(stream);
//now we are connected, so add a message to the queue //now we are connected, so add a message to the queue
AddMessage(new ConnectedMessage()); AddMessage(new ConnectedMessage());
//Join("MyRoom"); //Join("MyRoom");
@ -515,62 +529,74 @@ namespace VelNet
{ {
// Get a stream object for reading // Get a stream object for reading
//read a byte //read a byte
byte type = (byte)stream.ReadByte(); byte type = reader.ReadByte();
if (type == 0) //login switch (type)
{ {
LoginMessage m = new LoginMessage(); //login
m.userId = GetIntFromBytes(ReadExact(stream, 4)); //not really the sender... case 0:
AddMessage(m);
}
else if (type == 1) //rooms
{
RoomsMessage m = new RoomsMessage();
m.rooms = new List<ListedRoom>();
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)
{ {
string[] pieces = s.Split(':'); LoginMessage m = new LoginMessage();
if (pieces.Length == 2) m.userId = reader.ReadInt32(); //not really the sender...
{ AddMessage(m);
ListedRoom lr = new ListedRoom(); break;
lr.name = pieces[0];
lr.numUsers = int.Parse(pieces[1]);
m.rooms.Add(lr);
}
} }
//rooms
case 1:
{
RoomsMessage m = new RoomsMessage();
m.rooms = new List<ListedRoom>();
int N = reader.ReadInt32(); //the size of the payload
byte[] utf8data = reader.ReadBytes(N);
string roomMessage = Encoding.UTF8.GetString(utf8data);
AddMessage(m);
} string[] sections = roomMessage.Split(',');
else if (type == 2) //joined foreach (string s in sections)
{ {
JoinMessage m = new JoinMessage(); string[] pieces = s.Split(':');
m.userId = GetIntFromBytes(ReadExact(stream, 4)); if (pieces.Length == 2)
int N = stream.ReadByte(); {
byte[] utf8data = ReadExact(stream, N); //the room name, encoded as utf-8 ListedRoom lr = new ListedRoom();
m.room = Encoding.UTF8.GetString(utf8data); lr.name = pieces[0];
AddMessage(m); lr.numUsers = int.Parse(pieces[1]);
} m.rooms.Add(lr);
else if (type == 3) //data }
{ }
DataMessage m = new DataMessage();
m.senderId = GetIntFromBytes(ReadExact(stream, 4)); AddMessage(m);
int N = GetIntFromBytes(ReadExact(stream, 4)); //the size of the payload break;
m.data = ReadExact(stream, N); //the message }
AddMessage(m); //joined
} case 2:
else if (type == 4) //new master {
{ JoinMessage m = new JoinMessage();
ChangeMasterMessage m = new ChangeMasterMessage(); m.userId = reader.ReadInt32();
m.masterId = GetIntFromBytes(ReadExact(stream, 4)); //sender is the new master int N = reader.ReadByte();
AddMessage(m); 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;
}
} }
} }
} }
@ -684,15 +710,14 @@ namespace VelNet
} }
} }
/// <summary>
/// Connects to the server with a username
/// </summary>
///
public static byte[] get_be_bytes(int n) public static byte[] get_be_bytes(int n)
{ {
return BitConverter.GetBytes(n).Reverse().ToArray(); return BitConverter.GetBytes(n).Reverse().ToArray();
} }
/// <summary>
/// Connects to the server with a username
/// </summary>
public static void Login(string username, string password) public static void Login(string username, string password)
{ {
MemoryStream stream = new MemoryStream(); 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; byte sendType = (byte)MessageSendType.MESSAGE_OTHERS;
if (include_self && ordered) sendType = (byte)MessageSendType.MESSAGE_ALL_ORDERED; if (include_self && ordered) sendType = (byte)MessageSendType.MESSAGE_ALL_ORDERED;
@ -752,12 +777,12 @@ namespace VelNet
if (reliable) if (reliable)
{ {
MemoryStream stream = new MemoryStream(); MemoryStream mem = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream); BinaryWriter writer = new BinaryWriter(mem);
writer.Write(sendType); writer.Write(sendType);
writer.Write(get_be_bytes(message.Length)); writer.Write(get_be_bytes(message.Length));
writer.Write(message); writer.Write(message);
SendTcpMessage(stream.ToArray()); SendTcpMessage(mem.ToArray());
} }
else 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); byte[] utf8bytes = Encoding.UTF8.GetBytes(group);
if (reliable) if (reliable)
@ -845,8 +870,14 @@ namespace VelNet
newObject.owner = localPlayer; newObject.owner = localPlayer;
instance.objects.Add(newObject.networkId, newObject); instance.objects.Add(newObject.networkId, newObject);
// only sent to others, as I already instantiated this. Nice that it happens immediately. // 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; return newObject;
} }
@ -917,8 +948,13 @@ namespace VelNet
// immediately successful // immediately successful
instance.objects[networkId].owner = LocalPlayer; 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. // must be ordered, so that ownership transfers are not confused.
SendToRoom(Encoding.UTF8.GetBytes("6," + networkId)); // 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; return true;
} }

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using System.IO;
using System.Text; using System.Text;
namespace VelNet namespace VelNet
@ -40,7 +41,12 @@ namespace VelNet
{ {
if (kvp.Value.owner == this && kvp.Value.prefabName != "") 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
/// <summary> /// <summary>
/// These are generally things that come from the "owner" and should be enacted locally, where appropriate /// 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
/// </summary> /// </summary>
public void HandleMessage(VelNetManager.DataMessage m) 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); ushort numMessages = reader.ReadUInt16();
//types of messages for (int i = 0; i < numMessages; i++)
string[] messages = text.Split(';'); //messages are split by ;
foreach (string s in messages)
{ {
//individual message parameters separated by comma //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);
switch (sections[0]) // 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 (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 objectKey = messageReader.ReadString();
string identifier = sections[2]; string identifier = messageReader.ReadString();
string syncMessage = sections[3]; string syncMessage = messageReader.ReadString();
byte[] messageBytes = Convert.FromBase64String(syncMessage); byte[] messageBytes = Convert.FromBase64String(syncMessage);
if (manager.objects.ContainsKey(objectKey)) if (manager.objects.ContainsKey(objectKey))
{ {
@ -86,9 +104,9 @@ namespace VelNet
break; 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)) if (manager.objects.ContainsKey(networkId))
{ {
@ -97,10 +115,10 @@ namespace VelNet
break; 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 networkId = messageReader.ReadString();
string prefabName = sections[2]; string prefabName = messageReader.ReadString();
if (manager.objects.ContainsKey(networkId)) if (manager.objects.ContainsKey(networkId))
{ {
break; //we already have this one, ignore break; //we already have this one, ignore
@ -110,22 +128,25 @@ namespace VelNet
break; 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); VelNetManager.NetworkDestroy(networkId);
break; 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; break;
} }
default:
throw new ArgumentOutOfRangeException();
} }
} }
} }
@ -137,22 +158,39 @@ namespace VelNet
//FindObjectsOfType<NetworkObject>(); //FindObjectsOfType<NetworkObject>();
} }
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); using MemoryStream mem = new MemoryStream();
VelNetManager.SendToGroup(group, Encoding.UTF8.GetBytes(message), reliable); 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); using MemoryStream mem = new MemoryStream();
VelNetManager.SendToRoom(Encoding.UTF8.GetBytes(message), false, reliable); 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() public void SendSceneUpdate()
{ {
string message = "9," + string.Join(",", manager.deletedSceneObjects); using MemoryStream mem = new MemoryStream();
VelNetManager.SendToRoom( Encoding.UTF8.GetBytes(message)); 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.")] [Obsolete("Use VelNetManager.NetworkDestroy() instead.")]
@ -162,7 +200,12 @@ namespace VelNet
if (!manager.objects.ContainsKey(networkId) || manager.objects[networkId].owner != this || !isLocal) return; if (!manager.objects.ContainsKey(networkId) || manager.objects[networkId].owner != this || !isLocal) return;
// send to all, which will make me delete as well // 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);
} }
/// <returns>True if successful, False if failed to transfer ownership</returns> /// <returns>True if successful, False if failed to transfer ownership</returns>
@ -178,8 +221,13 @@ namespace VelNet
// immediately successful // immediately successful
manager.objects[networkId].owner = this; 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. // must be ordered, so that ownership transfers are not confused.
VelNetManager.SendToRoom(Encoding.UTF8.GetBytes("6," + networkId),true,true); // 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; return true;
} }