diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4ee3fdb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false + +[*.cs] +csharp_new_line_before_open_brace = none \ No newline at end of file diff --git a/ClientMsg.cs b/ClientMsg.cs new file mode 100644 index 0000000..020a975 --- /dev/null +++ b/ClientMsg.cs @@ -0,0 +1,73 @@ +namespace Passer.Control.Core { + + public class ClientMsg : IMessage { + public const byte Id = 0xA0; + public const byte length = 2; + public byte networkId; + + public ClientMsg(byte networkId) { + this.networkId = networkId; + } + + public ClientMsg(byte[] buffer) : base(buffer) { } + + public override byte Serialize(ref byte[] buffer) { + byte ix = 0; + buffer[ix++] = ClientMsg.Id; + buffer[ix++] = networkId; + return ClientMsg.length; + } + public override void Deserialize(byte[] buffer) { + base.Deserialize(buffer); + uint ix = 0; + networkId = buffer[ix]; + } + + public static async Task Receive(Stream dataStream, Participant 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)(Participant.clients.Count); + // NetworkIdMsg.Send(client, client.networkId); + client.messageQueue.Enqueue(msg); + } + else if (msg.networkId == 0) { + // NetworkIdMsg.Send(client, client.networkId); + client.messageQueue.Enqueue(msg); + } + + return true; + } + + public static byte Serialized(byte[] buffer, byte networkId) { + byte ix = 0; + buffer[ix++] = ClientMsg.Id; + buffer[ix++] = networkId; + return ix; + } + public static bool SendTo(Participant participant, byte networkId) { + if (participant == null) + return false; + + byte ix = 0; + participant.buffer[ix++] = ClientMsg.Id; + participant.buffer[ix++] = networkId; + + return participant.SendBuffer(ix); + } + public static bool Publish(Participant participant, byte networkId) { + if (participant == null) + return false; + + byte ix = 0; + participant.buffer[ix++] = ClientMsg.Id; + participant.buffer[ix++] = networkId; + + return participant.PublishBuffer(ix); + } + } +} \ No newline at end of file diff --git a/Messages.cs b/Messages.cs index 940eae9..2d27523 100644 --- a/Messages.cs +++ b/Messages.cs @@ -40,123 +40,6 @@ namespace Passer.Control.Core { } } - #region Client - - public class ClientMsg : IMessage { - public const byte Id = 0xA0; - public const byte length = 2; - public byte networkId; - - public ClientMsg(byte[] buffer) : base(buffer) { } - - public override void Deserialize(byte[] buffer) { - base.Deserialize(buffer); - uint ix = 0; - networkId = buffer[ix]; - } - - public static async Task Receive(Stream dataStream, Participant 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)(Participant.clients.Count); - // NetworkIdMsg.Send(client, client.networkId); - client.messageQueue.Enqueue(msg); - } - else if (msg.networkId == 0) { - // NetworkIdMsg.Send(client, client.networkId); - client.messageQueue.Enqueue(msg); - } - - return true; - } - - public static byte Serialized(byte[] buffer, byte networkId) { - byte ix = 0; - buffer[ix++] = ClientMsg.Id; - buffer[ix++] = networkId; - return ix; - } - public static bool SendTo(Participant participant, byte networkId) { - if (participant == null) - return false; - - byte ix = 0; - participant.buffer[ix++] = ClientMsg.Id; - participant.buffer[ix++] = networkId; - - return participant.SendBuffer(ix); - } - public static bool Publish(Participant participant, byte networkId) { - if (participant == null) - return false; - - byte ix = 0; - participant.buffer[ix++] = ClientMsg.Id; - participant.buffer[ix++] = networkId; - - return participant.PublishBuffer(ix); - } - } - - #endregion Client - - #region Network Id - - public class NetworkIdMsg : IMessage { - public const byte Id = 0xA1; - public const byte length = 2; - public byte networkId; - - NetworkIdMsg(byte networkId) { - this.networkId = networkId; - } - NetworkIdMsg(byte[] buffer) : base(buffer) { } - - public override byte Serialize(ref byte[] buffer) { - byte ix = 0; - buffer[ix++] = NetworkIdMsg.Id; - buffer[ix++] = this.networkId; - return ix; - } - public override void Deserialize(byte[] buffer) { - uint ix = 0; - this.networkId = buffer[ix]; - } - - public static bool Send(Participant client, byte networkId) { - NetworkIdMsg msg = new(networkId); - return SendMsg(client, msg); - } - public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { - if (packetSize != length) - return false; - - byte[] buffer = await Receive(dataStream, packetSize); - NetworkIdMsg msg = new(buffer); - client.messageQueue.Enqueue(msg); - return true; - } - - public static bool SendTo(Participant participant, byte networkId) { - if (participant == null) - return false; - - byte ix = 0; - participant.buffer[ix++] = NetworkIdMsg.Id; - participant.buffer[ix++] = networkId; - - return participant.SendBuffer(ix); - - } - } - - #endregion Network Id - #region Investigate public class InvestigateMsg : IMessage { @@ -204,249 +87,6 @@ namespace Passer.Control.Core { #endregion Investigate - #region Thing - - public class ThingMsg : IMessage { - public const byte length = 5; - public const byte id = 0x80; - public byte networkId; - public byte thingId; - public byte thingType; - public byte parentId; - - public ThingMsg(byte networkId, byte thingId, byte thingType, byte parentId) { - this.networkId = networkId; - this.thingId = thingId; - this.thingType = thingType; - this.parentId = parentId; - } - public ThingMsg(byte[] buffer) : base(buffer) { } - - public override byte Serialize(ref byte[] buffer) { - byte ix = 0; - buffer[ix++] = ThingMsg.id; - buffer[ix++] = this.networkId; - buffer[ix++] = this.thingId; - buffer[ix++] = this.thingType; - buffer[ix++] = this.parentId; - return ix; - } - public override void Deserialize(byte[] buffer) { - uint ix = 0; - this.networkId = buffer[ix++]; - this.thingId = buffer[ix++]; - this.thingType = buffer[ix++]; - this.parentId = buffer[ix]; - } - - public static bool Send(Participant client, Thing thing) { - ThingMsg msg = new(thing.networkId, thing.id, thing.type, thing.parent.id); - return SendMsg(client, msg); - } - //public static bool Send(Client client, byte networkId, byte thingId, byte thingType, byte parentId) - //{ - // ThingMsg msg = new(networkId, thingId, thingType, parentId); - // //UnityEngine.Debug.Log($"Send thing [{msg.networkId}/{msg.thingId}]"); - // return SendMsg(client, msg); - //} - public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { - if (packetSize != length) - return false; - - byte[] buffer = await Receive(dataStream, packetSize); - ThingMsg msg = new(buffer); - //UnityEngine.Debug.Log($"Receive thing [{msg.networkId}/{msg.thingId}]"); - - // Do no process poses with nwid == 0 (== local) - //if (msg.networkId == 0) - // return true; - - client.messageQueue.Enqueue(msg); - return true; - } - - public static bool SendTo(Participant participant, Thing thing) { - if (participant == null || thing == null) - return false; - - byte ix = 0; - participant.buffer[ix++] = ThingMsg.id; - participant.buffer[ix++] = participant.networkId; - participant.buffer[ix++] = thing.id; - participant.buffer[ix++] = thing.type; - if (thing.parent != null) - participant.buffer[ix++] = thing.parent.id; - else - participant.buffer[ix++] = 0; - - return participant.SendBuffer(ix); - } - } - - #endregion Thing - - #region Name - - public class NameMsg : IMessage { - public const byte Id = 0x91; // 145 - public const byte length = 4; - public byte networkId; - public byte thingId; - public byte len; - public string name; - - public NameMsg(byte networkId, byte thingId, string name) { - this.networkId = networkId; - this.thingId = thingId; - this.name = name; - } - public NameMsg(byte[] buffer) : base(buffer) { } - - public override byte Serialize(ref byte[] buffer) { - byte ix = 0; - buffer[ix++] = NameMsg.Id; - buffer[ix++] = this.networkId; - buffer[ix++] = this.thingId; - buffer[ix++] = (byte)this.name.Length; - for (int nameIx = 0; nameIx < this.name.Length; nameIx++) - buffer[ix++] = (byte)this.name[nameIx]; - return ix; - } - public override void Deserialize(byte[] buffer) { - byte ix = 0; - this.networkId = buffer[ix++]; - this.thingId = buffer[ix++]; - int strlen = buffer[ix++]; - this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); - } - - public static bool Send(Participant client, Thing thing) { - if (string.IsNullOrEmpty(thing.name)) - return true; // nothing sent, but still a success! - - NameMsg msg = new(thing.networkId, thing.id, thing.name); - return SendMsg(client, msg); - } - //public static bool Send(Client client, byte networkId, byte thingId, string name) - //{ - // NameMsg msg = new(networkId, thingId, name); - // return SendMsg(client, msg); - //} - public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { - byte[] buffer = await Receive(dataStream, packetSize); - NameMsg msg = new(buffer); - - client.messageQueue.Enqueue(msg); - return true; - } - - public static bool SendTo(Participant participant, Thing thing) { - if (participant == null || thing == null || thing.name == null) - return false; - - byte nameLength = (byte)thing.name.Length; - if (participant.buffer.Length < 3 + nameLength) - return false; - - byte ix = 0; - participant.buffer[ix++] = NameMsg.Id; - participant.buffer[ix++] = participant.networkId; - participant.buffer[ix++] = thing.id; - participant.buffer[ix++] = nameLength; - for (int nameIx = 0; nameIx < nameLength; nameIx++) - participant.buffer[ix++] = (byte)thing.name[nameIx]; - - return participant.SendBuffer(ix); - } - } - - #endregion - - #region Model URL - - public class ModelUrlMsg : IMessage { - public const byte Id = 0x90; // (144) Model URL - public byte networkId; - public byte thingId; - public Spherical position; - public float scale; - public string url; - - public ModelUrlMsg(byte networkId, byte thingId, string url, float scale = 1) { - this.networkId = networkId; - this.thingId = thingId; - this.url = url; - this.scale = scale; - this.position = Spherical.zero; - } - public ModelUrlMsg(byte[] buffer) : base(buffer) { } - - public override byte Serialize(ref byte[] buffer) { - byte ix = 0; - buffer[ix++] = ModelUrlMsg.Id; - buffer[ix++] = this.networkId; - buffer[ix++] = this.thingId; // Thing Id - LowLevelMessages.SendFloat16(buffer, ref ix, new float16(1.0f)); - - buffer[ix++] = (byte)url.Length; - for (int urlIx = 0; urlIx < this.url.Length; urlIx++) - buffer[ix++] = (byte)url[urlIx]; - return ix; - } - public override void Deserialize(byte[] buffer) { - byte ix = 0; - this.networkId = buffer[ix++]; - this.thingId = buffer[ix++]; - this.scale = LowLevelMessages.ReceiveFloat16(buffer, ref ix); - int strlen = buffer[ix++]; - url = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); - } - - public static bool Send(Participant client, Thing thing) { - if (string.IsNullOrEmpty(thing.modelUrl)) - return true; // nothing sent, but still a success! - - ModelUrlMsg msg = new(thing.networkId, thing.id, thing.modelUrl); - return SendMsg(client, msg); - } - public static bool Send(Participant client, byte networkId, byte thingId, string modelUrl) { - if (string.IsNullOrEmpty(modelUrl)) - return true; // nothing sent, but still a success! - - ModelUrlMsg msg = new(networkId, thingId, modelUrl); - return SendMsg(client, msg); - } - public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { - byte[] buffer = await Receive(dataStream, packetSize); - ModelUrlMsg msg = new(buffer); - client.messageQueue.Enqueue(msg); - return true; - } - - public static bool SendTo(Participant participant, Thing thing) { - if (participant == null || thing == null || thing.modelUrl == null) - return false; - - byte urlLength = (byte)thing.modelUrl.Length; - if (participant.buffer.Length < 3 + urlLength) - return false; - - byte ix = 0; - participant.buffer[ix++] = ModelUrlMsg.Id; - participant.buffer[ix++] = participant.networkId; - participant.buffer[ix++] = thing.id; // Thing Id - LowLevelMessages.SendFloat16(participant.buffer, ref ix, new float16(1.0f)); - - participant.buffer[ix++] = urlLength; - for (int urlIx = 0; urlIx < urlLength; urlIx++) - participant.buffer[ix++] = (byte)thing.modelUrl[urlIx]; - - return participant.SendBuffer(ix); - } - } - - #endregion Model URL - #region Pose public class PoseMsg : IMessage { diff --git a/ModelUrlMsg.cs b/ModelUrlMsg.cs new file mode 100644 index 0000000..47e99e7 --- /dev/null +++ b/ModelUrlMsg.cs @@ -0,0 +1,83 @@ +namespace Passer.Control.Core { + + public class ModelUrlMsg : IMessage { + public const byte Id = 0x90; // (144) Model URL + public const byte length = 4; + public byte networkId; + public byte thingId; + public string? url = null; + + public ModelUrlMsg(byte networkId, Thing thing) { + this.networkId = networkId; + this.thingId = thing.id; + this.url = thing.modelUrl; + } + public ModelUrlMsg(byte networkId, byte thingId, string url, float scale = 1) { + this.networkId = networkId; + this.thingId = thingId; + this.url = url; + } + public ModelUrlMsg(byte[] buffer) : base(buffer) { } + + public override byte Serialize(ref byte[] buffer) { + byte ix = 0; + buffer[ix++] = ModelUrlMsg.Id; + buffer[ix++] = this.networkId; + buffer[ix++] = this.thingId; // Thing Id + + buffer[ix++] = (byte)url.Length; + for (int urlIx = 0; urlIx < this.url.Length; urlIx++) + buffer[ix++] = (byte)url[urlIx]; + return ix; + } + public override void Deserialize(byte[] buffer) { + byte ix = 0; + this.networkId = buffer[ix++]; + this.thingId = buffer[ix++]; + int strlen = buffer[ix++]; + url = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); + } + + public static bool Send(Participant client, Thing thing) { + if (string.IsNullOrEmpty(thing.modelUrl)) + return true; // nothing sent, but still a success! + + ModelUrlMsg msg = new(thing.networkId, thing.id, thing.modelUrl); + return SendMsg(client, msg); + } + public static bool Send(Participant client, byte networkId, byte thingId, string modelUrl) { + if (string.IsNullOrEmpty(modelUrl)) + return true; // nothing sent, but still a success! + + ModelUrlMsg msg = new(networkId, thingId, modelUrl); + return SendMsg(client, msg); + } + public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { + byte[] buffer = await Receive(dataStream, packetSize); + ModelUrlMsg msg = new(buffer); + client.messageQueue.Enqueue(msg); + return true; + } + + public static bool SendTo(Participant participant, Thing thing) { + if (participant == null || thing == null || thing.modelUrl == null) + return false; + + byte urlLength = (byte)thing.modelUrl.Length; + if (participant.buffer.Length < 3 + urlLength) + return false; + + byte ix = 0; + participant.buffer[ix++] = ModelUrlMsg.Id; + participant.buffer[ix++] = participant.networkId; + participant.buffer[ix++] = thing.id; // Thing Id + + participant.buffer[ix++] = urlLength; + for (int urlIx = 0; urlIx < urlLength; urlIx++) + participant.buffer[ix++] = (byte)thing.modelUrl[urlIx]; + + return participant.SendBuffer(ix); + } + } + +} \ No newline at end of file diff --git a/NameMsg.cs b/NameMsg.cs new file mode 100644 index 0000000..18b3d5b --- /dev/null +++ b/NameMsg.cs @@ -0,0 +1,81 @@ +namespace Passer.Control.Core { + + public class NameMsg : IMessage { + public const byte Id = 0x91; // 145 + public const byte length = 4; + public byte networkId; + public byte thingId; + public byte len; + public string? name = null; + + public NameMsg(byte networkId, Thing thing) { + this.networkId = networkId; + this.thingId = thing.id; + this.name = thing.name; + } + public NameMsg(byte networkId, byte thingId, string name) { + this.networkId = networkId; + this.thingId = thingId; + this.name = name; + } + public NameMsg(byte[] buffer) : base(buffer) { } + + public override byte Serialize(ref byte[] buffer) { + byte ix = 0; + buffer[ix++] = NameMsg.Id; + buffer[ix++] = this.networkId; + buffer[ix++] = this.thingId; + buffer[ix++] = (byte)this.name.Length; + for (int nameIx = 0; nameIx < this.name.Length; nameIx++) + buffer[ix++] = (byte)this.name[nameIx]; + return ix; + } + public override void Deserialize(byte[] buffer) { + byte ix = 0; + this.networkId = buffer[ix++]; + this.thingId = buffer[ix++]; + int strlen = buffer[ix++]; + this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); + } + + public static bool Send(Participant client, Thing thing) { + if (string.IsNullOrEmpty(thing.name)) + return true; // nothing sent, but still a success! + + NameMsg msg = new(thing.networkId, thing.id, thing.name); + return SendMsg(client, msg); + } + //public static bool Send(Client client, byte networkId, byte thingId, string name) + //{ + // NameMsg msg = new(networkId, thingId, name); + // return SendMsg(client, msg); + //} + public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { + byte[] buffer = await Receive(dataStream, packetSize); + NameMsg msg = new(buffer); + + client.messageQueue.Enqueue(msg); + return true; + } + + public static bool SendTo(Participant participant, Thing thing) { + if (participant == null || thing == null || thing.name == null) + return false; + + byte nameLength = (byte)thing.name.Length; + if (participant.buffer.Length < 3 + nameLength) + return false; + + byte ix = 0; + participant.buffer[ix++] = NameMsg.Id; + participant.buffer[ix++] = participant.networkId; + participant.buffer[ix++] = thing.id; + participant.buffer[ix++] = nameLength; + for (int nameIx = 0; nameIx < nameLength; nameIx++) + participant.buffer[ix++] = (byte)thing.name[nameIx]; + + return participant.SendBuffer(ix); + } + } + +} \ No newline at end of file diff --git a/NetworkIdMsg.cs b/NetworkIdMsg.cs new file mode 100644 index 0000000..5c749ca --- /dev/null +++ b/NetworkIdMsg.cs @@ -0,0 +1,50 @@ +namespace Passer.Control.Core { + + public class NetworkIdMsg : IMessage { + public const byte Id = 0xA1; + public const byte length = 2; + public byte networkId; + + public NetworkIdMsg(byte networkId) { + this.networkId = networkId; + } + NetworkIdMsg(byte[] buffer) : base(buffer) { } + + public override byte Serialize(ref byte[] buffer) { + buffer[0] = NetworkIdMsg.Id; + buffer[1] = this.networkId; + return NetworkIdMsg.length; + } + public override void Deserialize(byte[] buffer) { + uint ix = 1; + this.networkId = buffer[ix]; + } + + public static bool Send(Participant client, byte networkId) { + NetworkIdMsg msg = new(networkId); + return SendMsg(client, msg); + } + public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + NetworkIdMsg msg = new(buffer); + client.messageQueue.Enqueue(msg); + return true; + } + + public static bool SendTo(Participant participant, byte networkId) { + if (participant == null) + return false; + + byte ix = 0; + participant.buffer[ix++] = NetworkIdMsg.Id; + participant.buffer[ix++] = networkId; + + return participant.SendBuffer(ix); + + } + } + +} \ No newline at end of file diff --git a/Participant.cs b/Participant.cs index 3e71974..a5a1547 100644 --- a/Participant.cs +++ b/Participant.cs @@ -5,15 +5,13 @@ using System.Net.Sockets; using System.IO; using System.Threading.Tasks; -namespace Passer.Control.Core -{ +namespace Passer.Control.Core { - public class Participant - { + public class Participant { //public ConnectionMethod connection; - public IPEndPoint endPoint; - public UdpClient udpClient; - public string ipAddress; + public IPEndPoint? endPoint = null; + public UdpClient? udpClient = null; + public string ipAddress = "0.0.0.0"; public string broadcastIpAddress = "255.255.255.255"; public int port; public Stream dataStream; @@ -24,10 +22,8 @@ namespace Passer.Control.Core public readonly ConcurrentQueue messageQueue = new(); - public Participant GetClient(string ipAddress, int port) - { - foreach (Participant c in others) - { + public Participant? GetClient(string ipAddress, int port) { + foreach (Participant c in others) { if (c.ipAddress == ipAddress && c.port == port) return c; } @@ -37,14 +33,26 @@ namespace Passer.Control.Core #region Init - public Participant() - { + public Participant() { this.dataStream = new EchoStream(); others.Add(this); } - public Participant(UdpClient udpClient, int port) : this() - { + /// + /// Create a new participant + /// + /// The ip address of the site server + /// The port number of the site server + public Participant(string ipAddress = "0.0.0.0", int port = 7681) : this() { + this.ipAddress = ipAddress; + this.port = port; + this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); // for sending + this.udpClient = new UdpClient(); // for receiving + this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); + this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); + } + + public Participant(UdpClient udpClient, int port) : this() { this.udpClient = udpClient; this.ipAddress = null; this.port = port; @@ -56,51 +64,88 @@ namespace Passer.Control.Core #region Update - private static readonly float publishInterval = 3.0f; - private float nextPublishMe = 0; + protected void ReceiveUDP(IAsyncResult result) { + if (udpClient == null || this.endPoint == null) + return; + + try { + byte[] data = udpClient.EndReceive(result, ref this.endPoint); + this.dataStream.WriteByte((byte)data.Length); + this.dataStream.Write(data, 0, data.Length); + Task task = Task.Run(() => ReceiveData()); + } + catch (Exception _) { + Console.WriteLine("connection error"); + } + udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); + } + + private static readonly long publishInterval = 3000; + private long nextPublishMe = 0; - public virtual void Update(float currentTime) - { - if (currentTime > this.nextPublishMe) - { - this.PublishBuffer(ClientMsg.Serialized(this.buffer, this.networkId)); - ClientMsg.Publish(this, this.networkId); - this.nextPublishMe = currentTime + Participant.publishInterval; + public virtual void Update(long currentTimeMs) { + if (currentTimeMs > this.nextPublishMe) { + this.Publish(new ClientMsg(this.networkId)); + Console.WriteLine($"Sent ClientMsg {this.networkId}"); + this.nextPublishMe = currentTimeMs + Participant.publishInterval; } - for (int ix = 0; ix < this.others.Count; ix++) - { + for (int ix = 0; ix < this.others.Count; ix++) { Participant client = this.others[ix]; if (client == null) continue; client.ProcessMessages(); } - Thing.UpdateAll(currentTime); + Thing.UpdateAll(currentTimeMs); } #endregion Update #region Send - public bool SendBuffer(int bufferSize) - { + public void SendThingInfo(Thing thing) { + this.Send(new ThingMsg(this.networkId, thing)); + this.Send(new NameMsg(this.networkId, thing)); + this.Send(new ModelUrlMsg(this.networkId, thing)); + } + + public bool Send(IMessage msg) { + int bufferSize = msg.Serialize(ref this.buffer); + if (bufferSize <= 0) + return true; + + Console.WriteLine($"msg to {endPoint.Address.ToString()} {endPoint.Port}"); + this.udpClient?.Send(this.buffer, bufferSize, this.endPoint); + return true; + } + + public bool Publish(IMessage msg) { + int bufferSize = msg.Serialize(ref this.buffer); + if (bufferSize <= 0) + return true; + + //Console.WriteLine($"publish to {broadcastIpAddress.ToString()} {this.port}"); + this.udpClient?.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port); + return true; + } + + public bool SendBuffer(int bufferSize) { //if (this.ipAddress == null) // return false; // UnityEngine.Debug.Log($"Send msg {buffer[0]} to {ipAddress}"); //this.udpClient.Send(this.buffer, bufferSize, this.ipAddress, this.port); - this.udpClient.Send(this.buffer, bufferSize, this.endPoint); + this.udpClient?.Send(this.buffer, bufferSize, this.endPoint); return true; } - public bool PublishBuffer(int bufferSize) - { + public bool PublishBuffer(int bufferSize) { if (this.broadcastIpAddress == null) return false; - this.udpClient.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port); + this.udpClient?.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port); return true; } @@ -108,10 +153,8 @@ namespace Passer.Control.Core #region Receive - public async Task ReceiveData() - { - while (true) - { + public async Task ReceiveData() { + while (true) { byte packetSize = (byte)this.dataStream.ReadByte(); if (packetSize != 0xFF) await ReceiveData(this.dataStream, this, packetSize); @@ -119,19 +162,16 @@ namespace Passer.Control.Core } } - public static async Task ReceiveData(Stream dataStream, Participant client, byte packetSize) - { + public static async Task ReceiveData(Stream dataStream, Participant client, byte packetSize) { byte msgId = (byte)dataStream.ReadByte(); - if (msgId == 0xFF) - { + if (msgId == 0xFF) { // Timeout return; } //UnityEngine.Debug.Log($"R {msgId} from {client.ipAddress}"); bool result = false; - switch (msgId) - { + switch (msgId) { case ClientMsg.Id: // 0xA0 / 160 result = await ClientMsg.Receive(dataStream, client, packetSize); break; @@ -165,8 +205,7 @@ namespace Passer.Control.Core default: break; } - if (result == false) - { + if (result == false) { packetSize = msgId; // skip 1 byte, msgId is possibly a packet size byte await ReceiveData(dataStream, client, packetSize); } @@ -176,16 +215,13 @@ namespace Passer.Control.Core #region Process - public virtual void ProcessMessages() - { + public virtual void ProcessMessages() { while (this.messageQueue.TryDequeue(out IMessage msg)) ProcessMessage(msg); } - public void ProcessMessage(IMessage msg) - { - switch (msg) - { + public void ProcessMessage(IMessage msg) { + switch (msg) { case ClientMsg clientMsg: ProcessClient(clientMsg); break; @@ -223,30 +259,41 @@ namespace Passer.Control.Core ForwardMessage(msg); } - protected virtual void ProcessClient(ClientMsg client) { } + protected virtual void ProcessClient(ClientMsg msg) { } - protected virtual void ProcessNetworkId(NetworkIdMsg networkId) { } + protected virtual void ProcessNetworkId(NetworkIdMsg msg) { + if (this.networkId != msg.networkId) { + this.networkId = msg.networkId; + Console.WriteLine($"receive network id {msg.networkId}"); + foreach (Thing thing in Thing.GetAllThings()) + this.SendThingInfo(thing); + } + } - protected virtual void ProcessInvestigate(InvestigateMsg investigate) { } + protected virtual void ProcessInvestigate(InvestigateMsg msg) { } - protected virtual void ProcessThing(ThingMsg thing) { } + protected virtual void ProcessThing(ThingMsg msg) { + Console.WriteLine($"received thing {msg.thingId}"); + } - protected virtual void ProcessName(NameMsg name) { } + protected virtual void ProcessName(NameMsg msg) { + Console.WriteLine($"received name {msg.name}"); + } - protected virtual void ProcessModelUrl(ModelUrlMsg modelUrl) { } + protected virtual void ProcessModelUrl(ModelUrlMsg msg) { + Console.WriteLine($"received name {msg.url}"); + } - protected virtual void ProcessPose(PoseMsg pose) { } + protected virtual void ProcessPose(PoseMsg msg) { } - protected virtual void ProcessCustom(CustomMsg custom) { } + protected virtual void ProcessCustom(CustomMsg msg) { } - protected virtual void ProcessText(TextMsg text) { } + protected virtual void ProcessText(TextMsg temsgxt) { } - protected virtual void ProcessDestroy(DestroyMsg destroy) { } + protected virtual void ProcessDestroy(DestroyMsg msg) { } - private void ForwardMessage(IMessage msg) - { - foreach (Participant client in others) - { + private void ForwardMessage(IMessage msg) { + foreach (Participant client in others) { if (client == this) continue; //UnityEngine.Debug.Log($"---> {client.ipAddress}"); diff --git a/SiteServer.cs b/SiteServer.cs index 7986caa..7caa93d 100644 --- a/SiteServer.cs +++ b/SiteServer.cs @@ -1,66 +1,87 @@ -using System.IO; -using System.Threading.Tasks; +using System.Net; +using System.Net.Sockets; namespace Passer.Control.Core { - public static class SiteServer { + public class SiteServer : Participant { + + public SiteServer(int port) { + this.ipAddress = "0.0.0.0"; + this.port = port; + this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), 0); // for sending, but should not be used really... + this.udpClient = new UdpClient(); // for receiving + this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port)); + this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); - public static async Task ReceiveData(Stream dataStream, Participant client) { - while (true) { - byte packetSize = (byte)dataStream.ReadByte(); - if (packetSize != 0xFF) - await ReceiveData(dataStream, client, packetSize); - // else timeout - } } - public static async Task ReceiveData(Stream dataStream, Participant client, byte packetSize) { - byte msgId = (byte)dataStream.ReadByte(); - if (msgId == 0xFF) { - // Timeout - return; - } - - //UnityEngine.Debug.Log($"R {msgId} from {client.ipAddress}"); - bool result = false; - switch (msgId) { - case ClientMsg.Id: // 0xA0 / 160 - result = await ClientMsg.Receive(dataStream, client, packetSize); - break; - case NetworkIdMsg.Id: // 0xA1 / 161 - result = await NetworkIdMsg.Receive(dataStream, client, packetSize); - break; - case InvestigateMsg.Id: // 0x81 - result = await InvestigateMsg.Receive(dataStream, client, packetSize); - break; - case ThingMsg.id: // 0x80 / 128 - result = await ThingMsg.Receive(dataStream, client, packetSize); - break; - case NameMsg.Id: // 0x91 / 145 - result = await NameMsg.Receive(dataStream, client, packetSize); - break; - case ModelUrlMsg.Id: // 0x90 / 144 - result = await ModelUrlMsg.Receive(dataStream, client, packetSize); - break; - case PoseMsg.Id: // 0x10 / 16 - result = await PoseMsg.Receive(dataStream, client, packetSize); - break; - case CustomMsg.Id: // 0xB1 / 177 - result = await CustomMsg.Receive(dataStream, client, packetSize); - break; - case TextMsg.Id: // 0xB0 / 176 - result = await TextMsg.Receive(dataStream, client, packetSize); - break; - case DestroyMsg.Id: // 0x20 / 32 - result = await DestroyMsg.Receive(dataStream, client, packetSize); - break; - default: - break; - } - if (result == false) { - packetSize = msgId; // skip 1 byte, msgId is possibly a packet size byte - await ReceiveData(dataStream, client, packetSize); + protected override void ProcessClient(ClientMsg msg) { + if (msg.networkId == 0) { + this.Send(new NetworkIdMsg(this.networkId)); + Console.WriteLine($"############################ New Client -> {this.networkId}"); } + else + Console.WriteLine($"############################ New Client"); } } + // public static class SiteServer { + + // public static async Task ReceiveData(Stream dataStream, Participant client) { + // while (true) { + // byte packetSize = (byte)dataStream.ReadByte(); + // if (packetSize != 0xFF) + // await ReceiveData(dataStream, client, packetSize); + // // else timeout + // } + // } + + // public static async Task ReceiveData(Stream dataStream, Participant client, byte packetSize) { + // byte msgId = (byte)dataStream.ReadByte(); + // if (msgId == 0xFF) { + // // Timeout + // return; + // } + + // //UnityEngine.Debug.Log($"R {msgId} from {client.ipAddress}"); + // bool result = false; + // switch (msgId) { + // case ClientMsg.Id: // 0xA0 / 160 + // result = await ClientMsg.Receive(dataStream, client, packetSize); + // break; + // case NetworkIdMsg.Id: // 0xA1 / 161 + // result = await NetworkIdMsg.Receive(dataStream, client, packetSize); + // break; + // case InvestigateMsg.Id: // 0x81 + // result = await InvestigateMsg.Receive(dataStream, client, packetSize); + // break; + // case ThingMsg.id: // 0x80 / 128 + // result = await ThingMsg.Receive(dataStream, client, packetSize); + // break; + // case NameMsg.Id: // 0x91 / 145 + // result = await NameMsg.Receive(dataStream, client, packetSize); + // break; + // case ModelUrlMsg.Id: // 0x90 / 144 + // result = await ModelUrlMsg.Receive(dataStream, client, packetSize); + // break; + // case PoseMsg.Id: // 0x10 / 16 + // result = await PoseMsg.Receive(dataStream, client, packetSize); + // break; + // case CustomMsg.Id: // 0xB1 / 177 + // result = await CustomMsg.Receive(dataStream, client, packetSize); + // break; + // case TextMsg.Id: // 0xB0 / 176 + // result = await TextMsg.Receive(dataStream, client, packetSize); + // break; + // case DestroyMsg.Id: // 0x20 / 32 + // result = await DestroyMsg.Receive(dataStream, client, packetSize); + // break; + // default: + // break; + // } + // if (result == false) { + // packetSize = msgId; // skip 1 byte, msgId is possibly a packet size byte + // await ReceiveData(dataStream, client, packetSize); + // } + // } + // } } \ No newline at end of file diff --git a/ThingMsg.cs b/ThingMsg.cs new file mode 100644 index 0000000..382fc8b --- /dev/null +++ b/ThingMsg.cs @@ -0,0 +1,86 @@ +namespace Passer.Control.Core { + + public class ThingMsg : IMessage { + public const byte length = 5; + public const byte id = 0x80; + public byte networkId; + public byte thingId; + public byte thingType; + public byte parentId; + + public ThingMsg(byte networkId, Thing thing) { + this.networkId = networkId; + this.thingId = thing.id; + this.thingType = thing.type; + this.parentId = thing.parent.id; + } + public ThingMsg(byte networkId, byte thingId, byte thingType, byte parentId) { + this.networkId = networkId; + this.thingId = thingId; + this.thingType = thingType; + this.parentId = parentId; + } + public ThingMsg(byte[] buffer) : base(buffer) { } + + public override byte Serialize(ref byte[] buffer) { + byte ix = 0; + buffer[ix++] = ThingMsg.id; + buffer[ix++] = this.networkId; + buffer[ix++] = this.thingId; + buffer[ix++] = this.thingType; + buffer[ix++] = this.parentId; + return ThingMsg.length; + } + public override void Deserialize(byte[] buffer) { + uint ix = 0; + this.networkId = buffer[ix++]; + this.thingId = buffer[ix++]; + this.thingType = buffer[ix++]; + this.parentId = buffer[ix]; + } + + public static bool Send(Participant client, Thing thing) { + ThingMsg msg = new(thing.networkId, thing.id, thing.type, thing.parent.id); + return SendMsg(client, msg); + } + //public static bool Send(Client client, byte networkId, byte thingId, byte thingType, byte parentId) + //{ + // ThingMsg msg = new(networkId, thingId, thingType, parentId); + // //UnityEngine.Debug.Log($"Send thing [{msg.networkId}/{msg.thingId}]"); + // return SendMsg(client, msg); + //} + public static async Task Receive(Stream dataStream, Participant client, byte packetSize) { + if (packetSize != length) + return false; + + byte[] buffer = await Receive(dataStream, packetSize); + ThingMsg msg = new(buffer); + //UnityEngine.Debug.Log($"Receive thing [{msg.networkId}/{msg.thingId}]"); + + // Do no process poses with nwid == 0 (== local) + //if (msg.networkId == 0) + // return true; + + client.messageQueue.Enqueue(msg); + return true; + } + + public static bool SendTo(Participant participant, Thing thing) { + if (participant == null || thing == null) + return false; + + byte ix = 0; + participant.buffer[ix++] = ThingMsg.id; + participant.buffer[ix++] = participant.networkId; + participant.buffer[ix++] = thing.id; + participant.buffer[ix++] = thing.type; + if (thing.parent != null) + participant.buffer[ix++] = thing.parent.id; + else + participant.buffer[ix++] = 0; + + return participant.SendBuffer(ix); + } + } + +} \ No newline at end of file diff --git a/test/UnitTest1.cs b/test/UnitTest1.cs index c4b42fd..e6e0d3e 100644 --- a/test/UnitTest1.cs +++ b/test/UnitTest1.cs @@ -2,16 +2,57 @@ using Passer.Control.Core; -public class Tests -{ +public class Tests { [SetUp] - public void Setup() - { + public void Setup() { } [Test] - public void Test1() - { + public void Test_Client() { + Participant participant = new("127.0.0.1", 7681); + + long milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + long startTime = milliseconds; + while (milliseconds < startTime + 7000) { + participant.Update(milliseconds); + milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + Thread.Sleep(100); + } + + Assert.Pass(); + } + + [Test] + public void Test_SiteServer() { + SiteServer siteServer = new(7681); + + long milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + long startTime = milliseconds; + while (milliseconds < startTime + 7000) { + siteServer.Update(milliseconds); + milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + Thread.Sleep(100); + } + + Assert.Pass(); + } + + [Test] + public void Test_ThingMsg() { + Participant participant = new("127.0.0.1"); + Thing thing = new() { + name = "First Thing", + modelUrl = "https://passer.life/extras/ant.jpg" + }; + + long milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + long startTime = milliseconds; + while (milliseconds < startTime + 7000) { + participant.Update(milliseconds); + milliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + Thread.Sleep(100); + } + Assert.Pass(); } }