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;
private DissonanceComms comms;
public void HandleSend()
{
if (sendInput.text != "")
{
VelNetManager.SendToRoom(Encoding.UTF8.GetBytes(sendInput.text));
}
}
public void HandleLogin()
{
if (userInput.text != "")

View File

@ -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);
}

View File

@ -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();
}
/// <summary>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}
/// <summary>
@ -473,10 +484,12 @@ namespace VelNet
}
}
/// <summary>
/// Runs in background clientReceiveThread; Listens for incoming data.
/// Reads N bytes
/// </summary>
/// <param name="stream"></param>
/// <param name="N"></param>
/// <returns></returns>
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<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)
//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<ListedRoom>();
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;
}
}
}
}
@ -684,15 +710,14 @@ namespace VelNet
}
}
/// <summary>
/// Connects to the server with a username
/// </summary>
///
public static byte[] get_be_bytes(int n)
{
return BitConverter.GetBytes(n).Reverse().ToArray();
}
/// <summary>
/// Connects to the server with a username
/// </summary>
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;
}

View File

@ -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
/// <summary>
/// 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>
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
string[] messages = text.Split(';'); //messages are split by ;
foreach (string s in messages)
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);
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 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<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);
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);
}
/// <returns>True if successful, False if failed to transfer ownership</returns>
@ -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;
}