Make the local things work (somewhat)

This commit is contained in:
Pascal Serrarens 2024-12-30 15:35:06 +01:00
parent e532f31236
commit 1429f0a9d6
15 changed files with 548 additions and 87 deletions

8
LinearAlgebra.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 028f10636fdaa594a9a9969924e5ff18
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
namespace Passer.LinearAlgebra {
public class Direction {
public float horizontal;
public float vertical;
public Direction() {
horizontal = 0;
vertical = 0;
}
public Direction(float horizontal, float vertical) {
this.horizontal = horizontal;
this.vertical = vertical;
//Normalize();
}
public readonly static Direction forward = new(0, 0);
public readonly static Direction backward = new(-180, 0);
public readonly static Direction up = new(0, 90);
public readonly static Direction down = new(0, -90);
public readonly static Direction left = new(-90, 0);
public readonly static Direction right = new(90, 0);
public void Normalize() {
if (this.vertical > 90 || this.vertical < -90) {
this.horizontal += 180;
this.vertical = 180 - this.vertical;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9975e4702de32624e8d3deaf84669f2f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -3,16 +3,18 @@ namespace Passer.LinearAlgebra
public class Spherical public class Spherical
{ {
public float distance; public float distance;
public float horizontal; public Direction direction;
public float vertical;
public static Spherical zero = new(0, 0, 0); public static Spherical zero = new(0, 0, 0);
public Spherical(float distance, float horizontal, float vertical) public Spherical(float distance, float horizontal, float vertical)
{ {
this.distance = distance; this.distance = distance;
this.horizontal = horizontal; this.direction = new Direction(horizontal, vertical);
this.vertical = vertical; }
public Spherical(float distance, Direction direction) {
this.distance = distance;
this.direction = direction;
} }
} }
} }

View File

@ -0,0 +1,20 @@
namespace Passer.LinearAlgebra {
public class SwingTwist {
public Direction swing;
public float twist;
public static readonly SwingTwist zero = new(0, 0, 0);
public SwingTwist(Direction swing, float twist) {
this.swing = swing;
this.twist = twist;
}
public SwingTwist(float horizontalSwing, float verticalSwing, float twist) {
this.swing = new Direction(horizontalSwing, verticalSwing);
this.swing.Normalize();
this.twist = twist;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 003a6ec763b101642b0589486f087aef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,17 +1,14 @@
using Passer.LinearAlgebra; using Passer.LinearAlgebra;
public class LowLevelMessages public class LowLevelMessages {
{
public static void SendSpherical(byte[] buffer, ref byte ix, Spherical v) public static void SendSpherical(byte[] buffer, ref byte ix, Spherical v) {
{
SendFloat16(buffer, ref ix, new float16(v.distance)); SendFloat16(buffer, ref ix, new float16(v.distance));
SendAngle8(buffer, ref ix, v.horizontal); SendAngle8(buffer, ref ix, v.direction.horizontal);
SendAngle8(buffer, ref ix, v.vertical); SendAngle8(buffer, ref ix, v.direction.horizontal);
} }
public static Spherical ReceiveSpherical(byte[] data, ref byte ix) public static Spherical ReceiveSpherical(byte[] data, ref byte ix) {
{
float distance = ReceiveFloat16(data, ref ix); float distance = ReceiveFloat16(data, ref ix);
float horizontal = ReceiveAngle8(data, ref ix); float horizontal = ReceiveAngle8(data, ref ix);
float vertical = ReceiveAngle8(data, ref ix); float vertical = ReceiveAngle8(data, ref ix);
@ -19,14 +16,18 @@ public class LowLevelMessages
return v; return v;
} }
public static void SendQuat32(byte[] buffer, ref byte ix, Quat32 q) public static void SendQuat32(byte[] buffer, ref byte ix, SwingTwist s) {
{ UnityEngine.Quaternion q = UnityEngine.Quaternion.Euler(-s.swing.vertical, s.swing.horizontal, s.twist);
Quat32 q32 = new(q.x, q.y, q.z, q.w);
SendQuat32(buffer, ref ix, q32);
}
public static void SendQuat32(byte[] buffer, ref byte ix, Quat32 q) {
int qx = (int)(q.x * 127 + 128); int qx = (int)(q.x * 127 + 128);
int qy = (int)(q.y * 127 + 128); int qy = (int)(q.y * 127 + 128);
int qz = (int)(q.z * 127 + 128); int qz = (int)(q.z * 127 + 128);
int qw = (int)(q.w * 255); int qw = (int)(q.w * 255);
if (q.w < 0) if (q.w < 0) {
{
qx = -qx; qx = -qx;
qy = -qy; qy = -qy;
qz = -qz; qz = -qz;
@ -38,8 +39,7 @@ public class LowLevelMessages
buffer[ix++] = (byte)qz; buffer[ix++] = (byte)qz;
buffer[ix++] = (byte)qw; buffer[ix++] = (byte)qw;
} }
public static Quat32 ReceiveQuat32(byte[] data, ref byte ix) public static Quat32 ReceiveQuat32(byte[] data, ref byte ix) {
{
Quat32 q = new( Quat32 q = new(
(data[ix++] - 128.0F) / 127.0F, (data[ix++] - 128.0F) / 127.0F,
(data[ix++] - 128.0F) / 127.0F, (data[ix++] - 128.0F) / 127.0F,
@ -48,8 +48,14 @@ public class LowLevelMessages
return q; return q;
} }
public static void SendAngle8(byte[] buffer, ref byte ix, float angle) public static SwingTwist ReceiveSwingTwist(byte[] data, ref byte ix) {
{ Quat32 q32 = ReceiveQuat32(data, ref ix);
UnityEngine.Quaternion q = new(q32.x, q32.y, q32.z, q32.w);
SwingTwist r = new(q.eulerAngles.y, q.eulerAngles.x, q.eulerAngles.z);
return r;
}
public static void SendAngle8(byte[] buffer, ref byte ix, float angle) {
// Normalize angle // Normalize angle
while (angle >= 180) while (angle >= 180)
angle -= 360; angle -= 360;
@ -58,8 +64,7 @@ public class LowLevelMessages
buffer[ix++] = (byte)((angle / 360.0f) * 256.0f); buffer[ix++] = (byte)((angle / 360.0f) * 256.0f);
} }
public static float ReceiveAngle8(byte[] data, ref byte ix) public static float ReceiveAngle8(byte[] data, ref byte ix) {
{
float value = (data[ix++] * 180) / 128.0F; float value = (data[ix++] * 180) / 128.0F;
return value; return value;
} }
@ -70,15 +75,13 @@ public class LowLevelMessages
data[ix++] = (byte)(binary >> 8); data[ix++] = (byte)(binary >> 8);
data[ix++] = (byte)(binary & 255); data[ix++] = (byte)(binary & 255);
} }
public static void SendFloat16(byte[] data, ref byte ix, float16 f) public static void SendFloat16(byte[] data, ref byte ix, float16 f) {
{
ushort binary = f.GetBinary(); ushort binary = f.GetBinary();
data[ix++] = (byte)(binary >> 8); data[ix++] = (byte)(binary >> 8);
data[ix++] = (byte)(binary & 255); data[ix++] = (byte)(binary & 255);
} }
public static float ReceiveFloat16(byte[] data, ref byte ix) public static float ReceiveFloat16(byte[] data, ref byte ix) {
{
ushort value = (ushort)(data[ix++] << 8 | data[ix++]); ushort value = (ushort)(data[ix++] << 8 | data[ix++]);
float16 f16 = new(); float16 f16 = new();
f16.SetBinary(value); f16.SetBinary(value);

View File

@ -2,7 +2,7 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Passer.LinearAlgebra; using Passer.LinearAlgebra;
namespace Passer.Control { namespace Passer.Control.Core {
public class IMessage { public class IMessage {
public IMessage() { } public IMessage() { }
@ -15,17 +15,17 @@ namespace Passer.Control {
public bool SendTo(Participant client) { public bool SendTo(Participant client) {
Serialize(ref client.buffer); Serialize(ref client.buffer);
return client.SendBuffer(); return client.SendBuffer(client.buffer.Length);
} }
public static bool SendMsg(Participant client, IMessage msg) { public static bool SendMsg(Participant client, IMessage msg) {
msg.Serialize(ref client.buffer); msg.Serialize(ref client.buffer);
return client.SendBuffer(); return client.SendBuffer(client.buffer.Length);
} }
public static bool PublishMsg(Participant client, IMessage msg) { public static bool PublishMsg(Participant client, IMessage msg) {
msg.Serialize(ref client.buffer); msg.Serialize(ref client.buffer);
return client.PublishBuffer(); return client.PublishBuffer(client.buffer.Length);
} }
public static async Task<byte[]> Receive(Stream dataStream, byte packetSize) { public static async Task<byte[]> Receive(Stream dataStream, byte packetSize) {
@ -68,10 +68,10 @@ namespace Passer.Control {
ClientMsg msg = new(networkId); ClientMsg msg = new(networkId);
return SendMsg(client, msg); return SendMsg(client, msg);
} }
public static bool Publish(Participant client, byte networkId) { //public static bool Publish(Participant client, byte networkId) {
ClientMsg msg = new(networkId); // ClientMsg msg = new(networkId);
return PublishMsg(client, msg); // return PublishMsg(client, msg);
} //}
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) { public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
if (packetSize != length) if (packetSize != length)
return false; return false;
@ -80,17 +80,38 @@ namespace Passer.Control {
ClientMsg msg = new(buffer); ClientMsg msg = new(buffer);
if (client.networkId == 0) { if (client.networkId == 0) {
client.networkId = (byte)(Participant.clients.Count); // client.networkId = (byte)(Participant.clients.Count);
NetworkIdMsg.Send(client, client.networkId); // NetworkIdMsg.Send(client, client.networkId);
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
} }
else if (msg.networkId == 0) { else if (msg.networkId == 0) {
NetworkIdMsg.Send(client, client.networkId); // NetworkIdMsg.Send(client, client.networkId);
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
} }
return true; return true;
} }
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 #endregion Client
@ -131,6 +152,18 @@ namespace Passer.Control {
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
return true; 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 #endregion Network Id
@ -186,7 +219,7 @@ namespace Passer.Control {
public class ThingMsg : IMessage { public class ThingMsg : IMessage {
public const byte length = 5; public const byte length = 5;
public const byte Id = 0x80; public const byte id = 0x80;
public byte networkId; public byte networkId;
public byte thingId; public byte thingId;
public byte thingType; public byte thingType;
@ -202,7 +235,7 @@ namespace Passer.Control {
public override byte Serialize(ref byte[] buffer) { public override byte Serialize(ref byte[] buffer) {
byte ix = 0; byte ix = 0;
buffer[ix++] = ThingMsg.Id; buffer[ix++] = ThingMsg.id;
buffer[ix++] = this.networkId; buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId; buffer[ix++] = this.thingId;
buffer[ix++] = this.thingType; buffer[ix++] = this.thingType;
@ -217,7 +250,7 @@ namespace Passer.Control {
this.parentId = buffer[ix]; this.parentId = buffer[ix];
} }
public static bool Send(Participant client, CoreThing thing) { public static bool Send(Participant client, Thing thing) {
ThingMsg msg = new(thing.networkId, thing.id, thing.type, thing.parent.id); ThingMsg msg = new(thing.networkId, thing.id, thing.type, thing.parent.id);
return SendMsg(client, msg); return SendMsg(client, msg);
} }
@ -242,6 +275,23 @@ namespace Passer.Control {
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
return true; 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 #endregion Thing
@ -281,7 +331,7 @@ namespace Passer.Control {
this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen);
} }
public static bool Send(Participant client, CoreThing thing) { public static bool Send(Participant client, Thing thing) {
if (string.IsNullOrEmpty(thing.name)) if (string.IsNullOrEmpty(thing.name))
return true; // nothing sent, but still a success! return true; // nothing sent, but still a success!
@ -300,6 +350,25 @@ namespace Passer.Control {
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
return true; 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 #endregion
@ -344,7 +413,7 @@ namespace Passer.Control {
url = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen); url = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen);
} }
public static bool Send(Participant client, CoreThing thing) { public static bool Send(Participant client, Thing thing) {
if (string.IsNullOrEmpty(thing.modelUrl)) if (string.IsNullOrEmpty(thing.modelUrl))
return true; // nothing sent, but still a success! return true; // nothing sent, but still a success!
@ -364,6 +433,27 @@ namespace Passer.Control {
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
return true; 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 #endregion Model URL
@ -383,11 +473,11 @@ namespace Passer.Control {
public const byte Pose_AngularVelocity = 0x08; public const byte Pose_AngularVelocity = 0x08;
public Spherical position; public Spherical position;
public Quat32 orientation; public SwingTwist orientation;
public Spherical linearVelocity; public Spherical linearVelocity;
public Spherical angularVelocity; public Spherical angularVelocity;
public PoseMsg(byte networkId, byte thingId, Spherical position, Quat32 orientation) { public PoseMsg(byte networkId, byte thingId, Spherical position, SwingTwist orientation) {
this.networkId = networkId; this.networkId = networkId;
this.thingId = thingId; this.thingId = thingId;
this.position = position; this.position = position;
@ -400,7 +490,7 @@ namespace Passer.Control {
if (this.orientation != null) if (this.orientation != null)
this.poseType |= Pose_Orientation; this.poseType |= Pose_Orientation;
else else
this.orientation = new Quat32(0, 0, 0, 1); this.orientation = SwingTwist.zero;
} }
public PoseMsg(byte[] buffer) : base(buffer) { } public PoseMsg(byte[] buffer) : base(buffer) { }
@ -411,8 +501,14 @@ namespace Passer.Control {
buffer[ix++] = this.thingId; buffer[ix++] = this.thingId;
buffer[ix++] = this.poseType; buffer[ix++] = this.poseType;
LowLevelMessages.SendSpherical(buffer, ref ix, this.position); if ((poseType & Pose_Position) != 0)
LowLevelMessages.SendQuat32(buffer, ref ix, this.orientation); LowLevelMessages.SendSpherical(buffer, ref ix, this.position);
if ((poseType & Pose_Orientation) != 0)
LowLevelMessages.SendQuat32(buffer, ref ix, this.orientation);
if ((poseType & Pose_LinearVelocity) != 0)
LowLevelMessages.SendSpherical(buffer, ref ix, this.linearVelocity);
if ((poseType & Pose_AngularVelocity) != 0)
LowLevelMessages.SendSpherical(buffer, ref ix, this.angularVelocity);
return ix; return ix;
} }
public override void Deserialize(byte[] buffer) { public override void Deserialize(byte[] buffer) {
@ -424,14 +520,14 @@ namespace Passer.Control {
if ((poseType & Pose_Position) != 0) if ((poseType & Pose_Position) != 0)
this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix); this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
if ((poseType & Pose_Orientation) != 0) if ((poseType & Pose_Orientation) != 0)
this.orientation = LowLevelMessages.ReceiveQuat32(buffer, ref ix); this.orientation = LowLevelMessages.ReceiveSwingTwist(buffer, ref ix);
if ((poseType & Pose_LinearVelocity) != 0) if ((poseType & Pose_LinearVelocity) != 0)
this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix); this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
if ((poseType & Pose_AngularVelocity) != 0) if ((poseType & Pose_AngularVelocity) != 0)
this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix); this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
} }
public static bool Send(Participant client, byte thingId, Spherical position, Quat32 orientation) { public static bool Send(Participant client, byte thingId, Spherical position, SwingTwist orientation) {
PoseMsg msg = new(client.networkId, thingId, position, orientation); PoseMsg msg = new(client.networkId, thingId, position, orientation);
return SendMsg(client, msg); return SendMsg(client, msg);
} }
@ -446,6 +542,28 @@ namespace Passer.Control {
client.messageQueue.Enqueue(msg); client.messageQueue.Enqueue(msg);
return true; return true;
} }
public static bool SendTo(Participant participant, Thing thing) {
if (participant == null || thing == null)
return false;
byte ix = 0;
participant.buffer[ix++] = PoseMsg.Id;
participant.buffer[ix++] = participant.networkId;
participant.buffer[ix++] = thing.id;
participant.buffer[ix++] = thing.poseUpdated;
if ((thing.poseUpdated & Pose_Position) != 0 && thing.position != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.position);
if ((thing.poseUpdated & Pose_Orientation) != 0 && thing.orientation != null)
LowLevelMessages.SendQuat32(participant.buffer, ref ix, thing.orientation);
if ((thing.poseUpdated & Pose_LinearVelocity) != 0 && thing.linearVelocity != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.linearVelocity);
if ((thing.poseUpdated & Pose_AngularVelocity) != 0 && thing.angularVelocity != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.angularVelocity);
return participant.SendBuffer(ix);
}
} }
#endregion Pose #endregion Pose

View File

@ -1,14 +1,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.IO; using System.IO;
using System.Threading.Tasks;
namespace Passer.Control namespace Passer.Control.Core
{ {
public class Participant public class Participant
{ {
//public ConnectionMethod connection; //public ConnectionMethod connection;
public IPEndPoint endPoint;
public UdpClient udpClient; public UdpClient udpClient;
public string ipAddress; public string ipAddress;
public string broadcastIpAddress = "255.255.255.255"; public string broadcastIpAddress = "255.255.255.255";
@ -21,45 +24,147 @@ namespace Passer.Control
public readonly ConcurrentQueue<IMessage> messageQueue = new(); public readonly ConcurrentQueue<IMessage> messageQueue = new();
public static Participant GetClient(string ipAddress, int port) public Participant GetClient(string ipAddress, int port)
{ {
foreach (Participant c in clients) foreach (Participant c in others)
{ {
if (c.ipAddress == ipAddress && c.port == port) if (c.ipAddress == ipAddress && c.port == port)
return c; return c;
} }
return null; return null;
} }
static public List<Participant> clients = new List<Participant>(); public List<Participant> others = new List<Participant>();
public Participant(UdpClient udpClient, int port) #region Init
public Participant() {
this.dataStream = new EchoStream();
others.Add(this);
}
public Participant(UdpClient udpClient, int port) : this()
{ {
this.udpClient = udpClient; this.udpClient = udpClient;
this.ipAddress = null; this.ipAddress = null;
this.port = port; this.port = port;
this.dataStream = new EchoStream(); //this.dataStream = new EchoStream();
clients.Add(this); //clients.Add(this);
} }
public bool SendBuffer() #endregion Init
{
if (this.ipAddress == null)
return false;
//UnityEngine.Debug.Log($"Send msg {buffer[0]} to {ipAddress}"); #region Update
this.udpClient.Send(this.buffer, this.buffer.Length, this.ipAddress, this.port);
private static readonly float publishInterval = 3.0f;
private float nextPublishMe = 0;
public virtual void Update(float currentTime) {
if (currentTime > this.nextPublishMe) {
ClientMsg.Publish(this, this.networkId);
this.nextPublishMe = currentTime + Participant.publishInterval;
}
for (int ix = 0; ix < this.others.Count; ix++) {
Participant client = this.others[ix];
if (client == null)
continue;
client.ProcessMessages();
}
Thing.UpdateAll(currentTime);
}
#endregion Update
#region Send
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);
return true; return true;
} }
public bool PublishBuffer() public bool PublishBuffer(int bufferSize)
{ {
if (this.broadcastIpAddress == null) if (this.broadcastIpAddress == null)
return false; return false;
this.udpClient.Send(this.buffer, this.buffer.Length, this.broadcastIpAddress, this.port); this.udpClient.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port);
return true; return true;
} }
#endregion
#region Receive
public async Task ReceiveData() {
while (true) {
byte packetSize = (byte)this.dataStream.ReadByte();
if (packetSize != 0xFF)
await ReceiveData(this.dataStream, this, 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);
}
}
#endregion
#region Process
public virtual void ProcessMessages() public virtual void ProcessMessages()
{ {
while (this.messageQueue.TryDequeue(out IMessage msg)) while (this.messageQueue.TryDequeue(out IMessage msg))
@ -129,7 +234,7 @@ namespace Passer.Control
private void ForwardMessage(IMessage msg) private void ForwardMessage(IMessage msg)
{ {
foreach (Participant client in Participant.clients) foreach (Participant client in others)
{ {
if (client == this) if (client == this)
continue; continue;
@ -137,5 +242,7 @@ namespace Passer.Control
IMessage.SendMsg(client, msg); IMessage.SendMsg(client, msg);
} }
} }
#endregion
} }
} }

View File

@ -1,7 +1,7 @@
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Passer.Control { namespace Passer.Control.Core {
public static class SiteServer { public static class SiteServer {
@ -33,7 +33,7 @@ namespace Passer.Control {
case InvestigateMsg.Id: // 0x81 case InvestigateMsg.Id: // 0x81
result = await InvestigateMsg.Receive(dataStream, client, packetSize); result = await InvestigateMsg.Receive(dataStream, client, packetSize);
break; break;
case ThingMsg.Id: // 0x80 / 128 case ThingMsg.id: // 0x80 / 128
result = await ThingMsg.Receive(dataStream, client, packetSize); result = await ThingMsg.Receive(dataStream, client, packetSize);
break; break;
case NameMsg.Id: // 0x91 / 145 case NameMsg.Id: // 0x91 / 145

187
Thing.cs
View File

@ -1,40 +1,189 @@
using System.Collections.Generic; using System.Collections.Generic;
using Passer.LinearAlgebra;
namespace Passer.Control namespace Passer.Control.Core {
{
public class CoreThing public class Thing {
{
public Participant participant; public Participant participant;
public delegate void ChangeHandler();
public delegate void SphericalHandler(Spherical v);
public byte networkId; public byte networkId;
public byte id; public byte id;
public CoreThing parent;
public List<CoreThing> children;
public byte type;
public string name;
public string modelUrl;
//protected Sensor sensor;
protected virtual void Init() public event ChangeHandler OnParentChanged;
{ private Thing _parent;
public Thing parent {
get => _parent;
set {
if (_parent == value)
return;
if (value == null) {
_parent.RemoveChild(this);
_parent = null;
} else {
value.AddChild(this);
OnParentChanged?.Invoke();
}
}
} }
public CoreThing(Participant client, byte networkId, byte thingId, byte thingType = 0) public void AddChild(Thing child) {
{ if (children.Find(thing => thing == child) != null)
return;
child._parent = this;
children.Add(child);
}
public void RemoveChild(Thing child) {
children.Remove(child);
}
[System.NonSerialized]
public List<Thing> children = new List<Thing>();
public byte type;
public event ChangeHandler OnNameChanged;
private string _name;
public string name {
get => _name;
set {
if (_name != value) {
_name = value;
OnNameChanged?.Invoke();
}
}
}
public string modelUrl;
public byte poseUpdated = 0x00;
public event ChangeHandler OnPositionChanged;
private Spherical _position;
public Spherical position {
get { return _position; }
set {
if (_position != value) {
_position = value;
OnPositionChanged?.Invoke();
}
}
}
public event ChangeHandler OnOrientationChanged;
private SwingTwist _orientation;
public SwingTwist orientation {
get { return _orientation; }
set {
if (_orientation != value) {
_orientation = value;
OnOrientationChanged?.Invoke();
}
}
}
public event SphericalHandler OnLinearVelocityChanged;
private Spherical _linearVelocity;
public Spherical linearVelocity {
get => _linearVelocity;
set {
if (_linearVelocity != value) {
_linearVelocity = value;
OnLinearVelocityChanged?.Invoke(_linearVelocity);
}
}
}
public Spherical angularVelocity;
public virtual void Init(bool invokeEvent = true) {
Thing.Add(this, invokeEvent);
}
public Thing(bool initialize = true) {
if (initialize) {
//this.Init();
Thing.Add(this);
}
}
public Thing(Participant client, byte networkId, byte thingId, byte thingType = 0) {
this.participant = client; this.participant = client;
this.id = thingId; this.id = thingId;
this.type = thingType; this.type = thingType;
this.networkId = networkId; this.networkId = networkId;
this.Init(); this.Init();
allThings.Add(this); Thing.Add(this);
} }
private static readonly List<CoreThing> allThings = new(); public virtual void Update(float currentTime) {
// should recurse over children...
}
public static CoreThing Get(byte networkId, byte thingId) public virtual void ProcessBytes(byte[] bytes) {
{ //if (sensor != null)
CoreThing thing = allThings.Find(aThing => aThing.networkId == networkId && aThing.id == thingId); // sensor.ProcessBytes(bytes);
}
// Experimental
public float stressLevel = 0;
protected delegate void ReceptorFunc(Sensor sensor);
protected void SetupReceptor(Sensor sensor, ReceptorFunc receptor) {
sensor.Signaller += (sensor => Receptor(receptor, sensor));
}
protected void Receptor(ReceptorFunc receptor, Sensor sensor) {
if (sensor.signalStrength <= stressLevel)
return;
receptor(sensor);
}
//---------- All Things
private static readonly List<Thing> allThings = new();
public delegate void ThingHandler(Thing t);
public static event ThingHandler OnNewThing;
public static void Add(Thing thing, bool invokeEvent = true) {
Thing foundThing = Get(thing.networkId, thing.id);
if (foundThing == null) {
if (thing.id == 0)
thing.id = (byte)(allThings.Count + 1);
allThings.Add(thing);
if (invokeEvent)
OnNewThing?.Invoke(thing);
UnityEngine.Debug.Log($"Add thing [{thing.networkId}/{thing.id}] {thing.name}");
}
}
public static Thing Get(byte networkId, byte thingId) {
Thing thing = allThings.Find(aThing => IsThing(aThing, networkId, thingId));
return thing; return thing;
} }
private static bool IsThing(Thing thing, byte networkId, byte thingId) {
if (thing == null)
return false;
return (thing.networkId == networkId ) && (thing.id == thingId);
}
public static void Remove(byte networkId, byte thingId) {
allThings.RemoveAll(t => t.networkId == networkId && t.id == thingId);
}
public static Thing[] GetAllThings() {
return allThings.ToArray();
}
public static void UpdateAll(float currentTime) {
foreach (Thing thing in allThings) {
if (thing.parent == null) // update only root things
thing.Update(currentTime);
}
}
} }
} }

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 0e8b48bc91446304eaaccbfdde4cc4af guid: 7429282ee0e367445bd1e2111631b27d
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2