From 6f12854d4f38d74448da4d51134281f9f28c7abf Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Thu, 5 Dec 2024 14:05:54 +0100 Subject: [PATCH] Completed messages from Unity Ants --- LowLevelMessages.cs | 35 +++ LowLevelMessages.cs.meta | 11 + Messages.cs | 466 ++++++++++++++++++++++++++++++--------- Messages.cs.meta | 11 + Quat32.cs | 15 ++ Quat32.cs.meta | 11 + README.md.meta | 7 + Spherical.cs | 15 ++ Spherical.cs.meta | 11 + 9 files changed, 473 insertions(+), 109 deletions(-) create mode 100644 LowLevelMessages.cs create mode 100644 LowLevelMessages.cs.meta create mode 100644 Messages.cs.meta create mode 100644 Quat32.cs create mode 100644 Quat32.cs.meta create mode 100644 README.md.meta create mode 100644 Spherical.cs create mode 100644 Spherical.cs.meta diff --git a/LowLevelMessages.cs b/LowLevelMessages.cs new file mode 100644 index 0000000..a976ddf --- /dev/null +++ b/LowLevelMessages.cs @@ -0,0 +1,35 @@ +using Passer; + +public class LowLevelMessages { + public static Spherical ReceiveSpherical(byte[] data, ref uint ix) { + float horizontal = ReceiveAngle8(data, ref ix); + float vertical = ReceiveAngle8(data, ref ix); + float distance = ReceiveFloat16(data, ref ix); + Spherical v = new(distance, horizontal, vertical); + return v; + } + + public static Quat32 ReceiveQuat32(byte[] data, ref uint ix) { + Quat32 q = new( + (data[ix++] - 128.0F) / 127.0F, + (data[ix++] - 128.0F) / 127.0F, + (data[ix++] - 128.0F) / 127.0F, + data[ix++] / 255.0F); + return q; + } + + public static float ReceiveAngle8(byte[] data, ref uint ix) { + float value = (data[ix++] * 180) / 128.0F; + return value; + } + + public static float ReceiveFloat16(byte[] data, ref uint ix) { + ushort value = (ushort)(data[ix++] << 8 | data[ix++]); + float16 f16 = new(); + f16.SetBinary(value); + float f = f16.toFloat(); + return f; + } + + +} diff --git a/LowLevelMessages.cs.meta b/LowLevelMessages.cs.meta new file mode 100644 index 0000000..9980c38 --- /dev/null +++ b/LowLevelMessages.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 157cf85b523c1f648adc007539f8b736 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Messages.cs b/Messages.cs index 7bbd7bc..eacfea6 100644 --- a/Messages.cs +++ b/Messages.cs @@ -1,120 +1,368 @@ using System.Collections.Generic; +using System.Collections.Concurrent; using System.IO; -using System.Net; using System.Net.Sockets; using System.Threading.Tasks; + namespace Passer.Control { -public class Client { - //public ConnectionMethod connection; - public UdpClient udpClient; - public string ipAddress; - public int port; - public byte networkId; + public class Client { + //public ConnectionMethod connection; + public UdpClient udpClient; + public string ipAddress; + public int port; - public static Client GetClient(string ipAddress, int port) { - foreach (Client c in clients) { - if (c.ipAddress == ipAddress && c.port == port) - return c; - } - return null; - } - static public List clients = new List(); + public byte networkId; - public static Client NewClient() { - Client client = new(); - clients.Add(client); - client.networkId = 0; + public readonly ConcurrentQueue messageQueue = new(); - return client; - } + public static Client GetClient(string ipAddress, int port) { + foreach (Client c in clients) { + if (c.ipAddress == ipAddress && c.port == port) + return c; + } + return null; + } + static public List clients = new List(); - public static Client NewUDPClient(UdpClient udpClient, string ipAddress, int port) { - Client client = NewClient(); - client.ipAddress = null; - client.port = port; - client.udpClient = udpClient; - return client; - } -} - -public class IMessage { - public IMessage() { } - public IMessage(byte[] data) { - Deserialize(data); - } - - public virtual byte[] Serialize() { return null; } - public virtual void Deserialize(byte[] data) { } - - public static void SendMsg(Client client, byte[] data) { - if (client.ipAddress != null) - client.udpClient.Send(data, data.Length, client.ipAddress, client.port); - else - ;//Debug.Log("Unknown client"); - } -} - -#region Client - -class ClientMsg : IMessage { - public const byte length = 2; - public byte clientId; - - public ClientMsg(byte[] data) : base(data) { } - public override void Deserialize(byte[] data) { - base.Deserialize(data); - uint ix = 0; - clientId = data[ix++]; - } - - protected async Task Receive(Stream dataStream, Client client, byte packetSize) { - if (packetSize != ClientMsg.length) { - //Debug.LogWarning($"Client msg size invalid: {packetSize}"); - return false; - } - - byte[] buffer = new byte[packetSize - 1]; // without msgId - int byteCount = dataStream.Read(buffer, 0, packetSize - 1); - while (byteCount < packetSize - 1) { - // not all bytes have been read, wait and try again - await Task.Delay(1); - byteCount += dataStream.Read(buffer, byteCount, packetSize - 1 - byteCount); - } - - ClientMsg msg = new(buffer); - - if (client.networkId == 0) { - client.networkId = (byte)(Client.clients.Count); - NetworkIdMsg.Send(client); - //if (string.IsNullOrEmpty(sceneUrl) == false) - //SendModelUrl(client, sceneUrl); - } - else if (msg.clientId == 0) { - NetworkIdMsg.Send(client); - //if (string.IsNullOrEmpty(sceneUrl) == false) - //SendModelUrl(client, sceneUrl); - } - - return true; - } -} - -#endregion Client - -#region Network Id - -class NetworkIdMsg : IMessage { - public const byte Id = 0xA1; - public const byte length = 2; - - public static void Send(Client client) { - byte[] data = new byte[NetworkIdMsg.length]; - data[0] = NetworkIdMsg.Id; - data[1] = client.networkId; - SendMsg(client, data); - } -} - -#endregion Network Id + public static Client NewClient() { + Client client = new(); + clients.Add(client); + client.networkId = 0; + + return client; + } + + public static Client NewUDPClient(UdpClient udpClient, string ipAddress, int port) { + Client client = NewClient(); + client.ipAddress = null; + client.port = port; + client.udpClient = udpClient; + return client; + } + } + + public class IMessage { + public IMessage() { } + public IMessage(byte[] data) { + Deserialize(data); + } + + public virtual byte[] Serialize() { return null; } + public virtual void Deserialize(byte[] data) { } + + public static bool SendMsg(Client client, IMessage msg) { + return SendMsg(client, msg.Serialize()); + } + public static bool SendMsg(Client client, byte[] data) { + if (client == null || client.ipAddress == null) + return false; + + client.udpClient.Send(data, data.Length, client.ipAddress, client.port); + return true; + } + public static async Task Receive(Stream dataStream, byte packetSize) { + byte[] buffer = new byte[packetSize - 1]; // without msgId + int byteCount = dataStream.Read(buffer, 0, packetSize - 1); + while (byteCount < packetSize - 1) { + // not all bytes have been read, wait and try again + await Task.Delay(1); + byteCount += dataStream.Read(buffer, byteCount, packetSize - 1 - byteCount); + } + return buffer; + } + } + + #region Client + + public class ClientMsg : IMessage { + public const byte length = 2; + public byte clientId; + + public ClientMsg(byte[] data) : base(data) { } + public override void Deserialize(byte[] data) { + base.Deserialize(data); + uint ix = 0; + clientId = data[ix++]; + } + + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + ClientMsg msg = new(buffer); + + if (client.networkId == 0) { + client.networkId = (byte)(Client.clients.Count); + NetworkIdMsg.Send(client); + //if (string.IsNullOrEmpty(sceneUrl) == false) + //SendModelUrl(client, sceneUrl); + } + else if (msg.clientId == 0) { + NetworkIdMsg.Send(client); + //if (string.IsNullOrEmpty(sceneUrl) == false) + //SendModelUrl(client, sceneUrl); + } + + return true; + } + } + + #endregion Client + + #region Network Id + + public class NetworkIdMsg : IMessage { + public const byte Id = 0xA1; + public const byte length = 2; + + public static bool Send(Client client) { + byte[] data = new byte[NetworkIdMsg.length]; + data[0] = NetworkIdMsg.Id; + data[1] = client.networkId; + return SendMsg(client, data); + } + } + + #endregion Network Id + + #region Thing + + public class ThingMsg : IMessage { + public const byte length = 4; + public const byte Id = 0x80; + public byte objectId; + public byte objectType; + public byte parentId; + + public ThingMsg(byte[] data) : base(data) { } + public override void Deserialize(byte[] data) { + uint ix = 0; + objectId = data[ix++]; + objectType = data[ix++]; + parentId = data[ix]; + } + + public static bool Send(Client client, Thing obj) { + if (obj == null) + return false; + + byte[] data = new byte[4]; + data[0] = ThingMsg.Id; + data[1] = obj.networkId; + data[2] = obj.thingId; + data[3] = obj.objectType; + data[4] = 0x00; // not supported yet + return SendMsg(client, data); + } + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + ThingMsg msg = new(buffer); + + client.messageQueue.Enqueue(msg); + return true; + } + } + + #endregion Thing + + #region Name + + public class NameMsg : IMessage { + public byte networkId = 0; + public byte objectId; + public byte len; + public string name; + + public NameMsg(byte[] data) : base(data) { } + public override void Deserialize(byte[] data) { + uint ix = 0; + this.objectId = data[ix++]; + int strlen = data[ix++]; + this.name = System.Text.Encoding.UTF8.GetString(data, (int)ix, strlen); + } + + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + byte[] buffer = await Receive(dataStream, packetSize); + NameMsg msg = new(buffer); + + client.messageQueue.Enqueue(msg); + return true; + } + } + + #endregion + + #region Model URL + + public class ModelUrlMsg : IMessage { + public const byte Id = 0x90; // (144) Model URL + public byte objectId; + public Spherical position; + public float scale; + public string url; + + public ModelUrlMsg(string url, float scale = 1) { + this.url = url; + this.scale = scale; + this.position = Spherical.zero; + } + public ModelUrlMsg(byte[] data) : base(data) { } + + public override byte[] Serialize() { + byte[] data = new byte[this.url.Length + 9]; + data[0] = ModelUrlMsg.Id; + data[1] = 0x00; // Thing Id + // data[2]..[5] == position 0, 0, 0 + data[6] = 0x3C; // Dummy float16 value 1 + data[7] = 0x00; + + data[8] = (byte)url.Length; + for (int ix = 0; ix < this.url.Length; ix++) + data[9 + ix] = (byte)url[ix]; + return data; + } + public override void Deserialize(byte[] data) { + uint ix = 0; + this.objectId = data[ix++]; + this.position = LowLevelMessages.ReceiveSpherical(data, ref ix); + this.scale = LowLevelMessages.ReceiveFloat16(data, ref ix); + int strlen = data[ix++]; + url = System.Text.Encoding.UTF8.GetString(data, (int)ix, strlen); + } + + public static bool Send(Client client, string modelUrl) { + ModelUrlMsg msg = new(modelUrl); + return SendMsg(client, msg); + } + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + byte[] buffer = await Receive(dataStream, packetSize); + ModelUrlMsg msg = new(buffer); + client.messageQueue.Enqueue(msg); + return true; + } + } + + #endregion Model URL + + #region Pose + + public class PoseMsg : IMessage { + public const byte length = 3 + 4 + 4; + public byte thingId; + public byte poseType; + + public Spherical position; + public Quat32 orientation; + + public PoseMsg(byte[] data) : base(data) { } + + public override void Deserialize(byte[] data) { + uint ix = 0; + thingId = data[ix++]; + poseType = data[ix++]; + + //if ((poseType & Pose_Position) != 0) + position = LowLevelMessages.ReceiveSpherical(data, ref ix); + //if ((poseType & Pose_Orientation) != 0) { + orientation = LowLevelMessages.ReceiveQuat32(data, ref ix); + } + + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + PoseMsg msg = new(buffer); + + client.messageQueue.Enqueue(msg); + return true; + + } + } + + #endregion Pose + + #region Bytes + + public class BytesMsg : IMessage { + public const byte Id = 0xB1; + public byte networkId; + public byte thingId; + public byte[] bytes; + + public BytesMsg(byte[] data) : base(data) { } + public BytesMsg(byte networkId, byte thingId, byte[] bytes) : base() { + this.networkId = networkId; + this.thingId = thingId; + this.bytes = bytes; + } + public override byte[] Serialize() { + byte[] buffer = new byte[4 + this.bytes.Length]; + int ix = 0; + buffer[ix++] = BytesMsg.Id; + buffer[ix++] = this.networkId; + buffer[ix++] = this.thingId; + buffer[ix++] = (byte)bytes.Length; + foreach (byte b in bytes) + buffer[ix++] = b; + + return buffer; + } + public override void Deserialize(byte[] data) { + //this.bytes = data; + uint ix = 0; + this.thingId = data[ix++]; + this.bytes = new byte[data.Length - ix]; + for (uint bytesIx = 0; ix < data.Length; ix++, bytesIx++) + this.bytes[bytesIx] = data[ix]; + } + + + public static void Send(Client client, byte thingId, byte[] bytes) { + BytesMsg msg = new(client.networkId, thingId, bytes); + SendMsg(client, msg); + } + + // received bytes + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + byte[] buffer = await Receive(dataStream, packetSize); + BytesMsg msg = new(buffer); + client.messageQueue.Enqueue(msg); + return true; + } + } + + #endregion Bytes + + #region Destroy + + public class DestroyMsg : IMessage { + public const byte length = 2; + public byte objectId; + + public DestroyMsg(byte[] data) : base(data) { } + + public override void Deserialize(byte[] data) { + objectId = data[0]; + } + + public static async Task Receive(Stream dataStream, Client client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + DestroyMsg msg = new(buffer); + + client.messageQueue.Enqueue(msg); + return true; + } + } + + + #endregion Destroy } diff --git a/Messages.cs.meta b/Messages.cs.meta new file mode 100644 index 0000000..60016bd --- /dev/null +++ b/Messages.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3f26f22a5422a44997b39a1844ab2ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Quat32.cs b/Quat32.cs new file mode 100644 index 0000000..dc6cbc4 --- /dev/null +++ b/Quat32.cs @@ -0,0 +1,15 @@ +namespace Passer { + public class Quat32 { + public float x; + public float y; + public float z; + public float w; + + public Quat32(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + } +} \ No newline at end of file diff --git a/Quat32.cs.meta b/Quat32.cs.meta new file mode 100644 index 0000000..c3f81a4 --- /dev/null +++ b/Quat32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96aa3cf18eaeb574d9265704d68000da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..f345b71 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3b79b5b373e9ced4abe72eb4d2f83c6a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Spherical.cs b/Spherical.cs new file mode 100644 index 0000000..3ecb663 --- /dev/null +++ b/Spherical.cs @@ -0,0 +1,15 @@ +namespace Passer { + public class Spherical { + public float distance; + public float horizontal; + public float vertical; + + public static Spherical zero = new(0, 0, 0); + + public Spherical(float distance, float horizontal, float vertical) { + this.distance = distance; + this.horizontal = horizontal; + this.vertical = vertical; + } + } +} \ No newline at end of file diff --git a/Spherical.cs.meta b/Spherical.cs.meta new file mode 100644 index 0000000..5ba10b9 --- /dev/null +++ b/Spherical.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33f05ee51ff6f7042b3e22723bcfc4f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: