Merge commit '383f0c179303e58d859ffe4fb4c05170bed3b1ed' into V2
This commit is contained in:
commit
4eb65f1312
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,4 +4,4 @@ test/bin
|
||||
test/obj
|
||||
/bin
|
||||
/obj
|
||||
*.meta
|
||||
**.meta
|
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f964f406734cf74097d61697e5cafc9
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -47,10 +47,7 @@ namespace Passer.RoboidControl {
|
||||
this.thingId = thing.id;
|
||||
this.bytes = System.Array.Empty<byte>();
|
||||
}
|
||||
/// <summary>
|
||||
/// Create the message for receiving
|
||||
/// </summary>
|
||||
/// <param name="buffer">The byte array to parse</param>
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public BinaryMsg(byte[] buffer) {
|
||||
byte ix = 1;
|
||||
this.networkId = buffer[ix++];
|
||||
@ -61,13 +58,9 @@ namespace Passer.RoboidControl {
|
||||
this.bytes[bytesIx] = buffer[ix++];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the message into a byte array for sending
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to serilize into</param>
|
||||
/// <returns>The length of the message in the buffer</returns>
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
if (buffer.Length < BinaryMsg.length + buffer.Length || bytes.Length == 0)
|
||||
if (buffer.Length < BinaryMsg.length + this.bytes.Length || this.bytes.Length == 0)
|
||||
return 0;
|
||||
|
||||
byte ix = 0;
|
||||
|
@ -3,29 +3,52 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Message notifiying that a Thing no longer exists
|
||||
/// </summary>
|
||||
public class DestroyMsg : IMessage {
|
||||
/// <summary>
|
||||
/// The message ID
|
||||
/// </summary>
|
||||
public const byte Id = 0x20;
|
||||
/// <summary>
|
||||
/// The length of the message
|
||||
/// </summary>
|
||||
public const byte length = 3;
|
||||
/// <summary>
|
||||
/// The network ID of the thing
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
/// <summary>
|
||||
/// The ID of the thing
|
||||
/// </summary>
|
||||
public byte thingId;
|
||||
|
||||
/// <summary>
|
||||
/// Create a message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID of the thing</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
public DestroyMsg(byte networkId, byte thingId) {
|
||||
this.networkId = networkId;
|
||||
this.thingId = thingId;
|
||||
}
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public DestroyMsg(byte[] buffer) : base(buffer) { }
|
||||
|
||||
public override void Deserialize(byte[] buffer) {
|
||||
this.networkId = buffer[0];
|
||||
this.thingId = buffer[1];
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
if (buffer.Length < DestroyMsg.length)
|
||||
return 0;
|
||||
|
||||
byte ix = 0;
|
||||
buffer[ix++] = DestroyMsg.Id;
|
||||
buffer[ix++] = this.networkId;
|
||||
buffer[ix++] = this.thingId;
|
||||
|
||||
return ix;
|
||||
}
|
||||
|
||||
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
if (packetSize != length)
|
||||
return false;
|
||||
|
||||
byte[] buffer = await Receive(dataStream, packetSize);
|
||||
DestroyMsg msg = new DestroyMsg(buffer);
|
||||
|
||||
client.messageQueue.Enqueue(msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,18 +3,40 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Message to request details for a Thing
|
||||
/// </summary>
|
||||
public class InvestigateMsg : IMessage {
|
||||
/// <summary>
|
||||
/// The message Id
|
||||
/// </summary>
|
||||
public const byte Id = 0x81;
|
||||
/// <summary>
|
||||
/// The length of the message
|
||||
/// </summary>
|
||||
public const byte length = 3;
|
||||
/// <summary>
|
||||
/// The network ID of the thing
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
/// <summary>
|
||||
/// The ID of the thing
|
||||
/// </summary>
|
||||
public byte thingId;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID for the thing</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
public InvestigateMsg(byte networkId, byte thingId) {
|
||||
this.networkId = networkId;
|
||||
this.thingId = thingId;
|
||||
}
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public InvestigateMsg(byte[] buffer) : base(buffer) { }
|
||||
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
byte ix = 0;
|
||||
buffer[ix++] = InvestigateMsg.Id;
|
||||
@ -22,28 +44,28 @@ namespace Passer.RoboidControl {
|
||||
buffer[ix++] = this.thingId;
|
||||
return ix;
|
||||
}
|
||||
public override void Deserialize(byte[] buffer) {
|
||||
uint ix = 0;
|
||||
this.networkId = buffer[ix++];
|
||||
this.thingId = buffer[ix++];
|
||||
}
|
||||
// public override void Deserialize(byte[] buffer) {
|
||||
// uint ix = 0;
|
||||
// this.networkId = buffer[ix++];
|
||||
// this.thingId = buffer[ix++];
|
||||
// }
|
||||
|
||||
//public static bool Send(Participant client, CoreThing thing) {
|
||||
// InvestigateMsg msg = new(thing.networkId, thing.id);
|
||||
// return SendMsg(client, msg);
|
||||
//}
|
||||
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
if (packetSize != length)
|
||||
return false;
|
||||
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
// if (packetSize != length)
|
||||
// return false;
|
||||
|
||||
byte[] buffer = await Receive(dataStream, packetSize);
|
||||
InvestigateMsg msg = new InvestigateMsg(buffer);
|
||||
//UnityEngine.Debug.Log($"Receive investigate [{msg.networkId}/{msg.thingId}]");
|
||||
// byte[] buffer = await Receive(dataStream, packetSize);
|
||||
// InvestigateMsg msg = new InvestigateMsg(buffer);
|
||||
// //UnityEngine.Debug.Log($"Receive investigate [{msg.networkId}/{msg.thingId}]");
|
||||
|
||||
client.messageQueue.Enqueue(msg);
|
||||
return true;
|
||||
// client.messageQueue.Enqueue(msg);
|
||||
// return true;
|
||||
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -3,40 +3,54 @@ using System.IO;
|
||||
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Root structure for all communcation messages
|
||||
/// </summary>
|
||||
public class IMessage {
|
||||
public IMessage() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a message for receiving
|
||||
/// </summary>
|
||||
/// <param name="buffer">The byte array to parse</param>
|
||||
public IMessage(byte[] buffer) {
|
||||
Deserialize(buffer);
|
||||
//Deserialize(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the message into a byte array for sending
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to serilize into</param>
|
||||
/// <returns>The length of the message in the buffer</returns>
|
||||
public virtual byte Serialize(ref byte[] buffer) { return 0; }
|
||||
public virtual void Deserialize(byte[] buffer) { }
|
||||
|
||||
public bool SendTo(Participant client) {
|
||||
Serialize(ref client.buffer);
|
||||
return client.SendBuffer(client.buffer.Length);
|
||||
}
|
||||
//public virtual void Deserialize(byte[] buffer) { }
|
||||
|
||||
public static bool SendMsg(Participant client, IMessage msg) {
|
||||
msg.Serialize(ref client.buffer);
|
||||
return client.SendBuffer(client.buffer.Length);
|
||||
}
|
||||
// public bool SendTo(Participant client) {
|
||||
// Serialize(ref client.buffer);
|
||||
// return client.SendBuffer(client.buffer.Length);
|
||||
// }
|
||||
|
||||
public static bool PublishMsg(Participant client, IMessage msg) {
|
||||
msg.Serialize(ref client.buffer);
|
||||
return client.PublishBuffer(client.buffer.Length);
|
||||
}
|
||||
// public static bool SendMsg(Participant client, IMessage msg) {
|
||||
// msg.Serialize(ref client.buffer);
|
||||
// return client.SendBuffer(client.buffer.Length);
|
||||
// }
|
||||
|
||||
public static async Task<byte[]> 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;
|
||||
}
|
||||
// public static bool PublishMsg(Participant client, IMessage msg) {
|
||||
// msg.Serialize(ref client.buffer);
|
||||
// return client.PublishBuffer(client.buffer.Length);
|
||||
// }
|
||||
|
||||
// public static async Task<byte[]> 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;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,23 +1,56 @@
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Message for communicating the name of a thing
|
||||
/// </summary>
|
||||
public class NameMsg : IMessage {
|
||||
/// <summary>
|
||||
/// The message ID
|
||||
/// </summary>
|
||||
public const byte Id = 0x91; // 145
|
||||
/// <summary>
|
||||
/// The length of the message
|
||||
/// </summary>
|
||||
public const byte length = 4;
|
||||
/// <summary>
|
||||
/// The network ID of the thing
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
/// <summary>
|
||||
/// The ID of the thing
|
||||
/// </summary>
|
||||
public byte thingId;
|
||||
/// <summary>
|
||||
/// The length of the name, excluding null terminator
|
||||
/// </summary>
|
||||
public byte len;
|
||||
/// <summary>
|
||||
/// The name of the thing, not terminated with a null character
|
||||
/// </summary>
|
||||
public string name = "";
|
||||
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID of the thing</param>
|
||||
/// <param name="thing">The thing</param>
|
||||
public NameMsg(byte networkId, Thing thing) {
|
||||
this.networkId = networkId;
|
||||
this.thingId = thing.id;
|
||||
this.name = thing.name;
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID of the thing</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
/// <param name="name">The name of the thing</param>
|
||||
public NameMsg(byte networkId, byte thingId, string name) {
|
||||
this.networkId = networkId;
|
||||
this.thingId = thingId;
|
||||
this.name = name;
|
||||
}
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public NameMsg(byte[] buffer) {
|
||||
byte ix = 1;
|
||||
this.networkId = buffer[ix++];
|
||||
@ -26,8 +59,9 @@ namespace Passer.RoboidControl {
|
||||
this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen);
|
||||
}
|
||||
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
if (this.name.Length == 0)
|
||||
if (buffer.Length < NameMsg.length + this.name.Length || this.name.Length == 0)
|
||||
return 0;
|
||||
|
||||
byte ix = 0;
|
||||
@ -40,6 +74,7 @@ namespace Passer.RoboidControl {
|
||||
buffer[ix++] = (byte)nameLength;
|
||||
for (int nameIx = 0; nameIx < nameLength; nameIx++)
|
||||
buffer[ix++] = (byte)this.name[nameIx];
|
||||
|
||||
return ix;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,39 @@
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// A message communicating the network ID for that participant
|
||||
/// </summary>
|
||||
public class NetworkIdMsg : IMessage {
|
||||
/// <summary>
|
||||
/// The message ID
|
||||
/// </summary>
|
||||
public const byte Id = 0xA1;
|
||||
/// <summary>
|
||||
/// The length of the message
|
||||
/// </summary>
|
||||
public const byte length = 2;
|
||||
/// <summary>
|
||||
/// The network ID for the participant
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID for the participant</param>
|
||||
public NetworkIdMsg(byte networkId) {
|
||||
this.networkId = networkId;
|
||||
}
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public NetworkIdMsg(byte[] buffer) {
|
||||
this.networkId = buffer[1];
|
||||
}
|
||||
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
if (buffer.Length < NetworkIdMsg.length)
|
||||
return 0;
|
||||
|
||||
buffer[0] = NetworkIdMsg.Id;
|
||||
buffer[1] = this.networkId;
|
||||
return NetworkIdMsg.length;
|
||||
|
@ -4,23 +4,73 @@ using Passer.LinearAlgebra;
|
||||
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Message to communicate the pose of the thing
|
||||
/// </summary>
|
||||
/// The pose is in local space relative to the parent. If there is not parent (the thing is a root thing), the pose will be in world space.
|
||||
public class PoseMsg : IMessage {
|
||||
/// <summary>
|
||||
/// The message ID
|
||||
/// </summary>
|
||||
public const byte Id = 0x10;
|
||||
/// <summary>
|
||||
/// The length of the message
|
||||
/// </summary>
|
||||
public const byte length = 4 + 4 + 4;
|
||||
/// <summary>
|
||||
/// The network ID of the thing
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
/// <summary>
|
||||
/// The ID of the thing
|
||||
/// </summary>
|
||||
public byte thingId;
|
||||
|
||||
/// <summary>
|
||||
/// bitpattern stating which pose components are available
|
||||
/// </summary>
|
||||
public byte poseType;
|
||||
/// <summary>
|
||||
/// Bit pattern for a pose with position
|
||||
/// </summary>
|
||||
public const byte Pose_Position = 0x01;
|
||||
/// <summary>
|
||||
/// Bit pattern for a pose with orientation
|
||||
/// </summary>
|
||||
public const byte Pose_Orientation = 0x02;
|
||||
/// <summary>
|
||||
/// Bit pattern for a pose with linear velocity
|
||||
/// </summary>
|
||||
public const byte Pose_LinearVelocity = 0x04;
|
||||
/// <summary>
|
||||
/// Bit pattern for a pose with angular velocity
|
||||
/// </summary>
|
||||
public const byte Pose_AngularVelocity = 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// The position of the thing in local space in meters
|
||||
/// </summary>
|
||||
public Spherical position = Spherical.zero;
|
||||
/// <summary>
|
||||
/// The orientation of the thing in local space
|
||||
/// </summary>
|
||||
public SwingTwist orientation = SwingTwist.zero;
|
||||
/// <summary>
|
||||
/// The linear velocity of the thing in local space in meters per second
|
||||
/// </summary>
|
||||
public Spherical linearVelocity = Spherical.zero;
|
||||
/// <summary>
|
||||
/// The angular veloicty of the thing in local space
|
||||
/// </summary>
|
||||
public Spherical angularVelocity = Spherical.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="networkId">The network ID of the thing</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
/// <param name="position">The position of the thing in local space in meters</param>
|
||||
/// <param name="orientation">The orientation of the thing in local space</param>
|
||||
public PoseMsg(byte networkId, byte thingId, Spherical position, SwingTwist orientation) {
|
||||
this.networkId = networkId;
|
||||
this.thingId = thingId;
|
||||
@ -34,8 +84,10 @@ namespace Passer.RoboidControl {
|
||||
if (this.orientation != null)
|
||||
this.poseType |= Pose_Orientation;
|
||||
}
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public PoseMsg(byte[] buffer) : base(buffer) { }
|
||||
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
byte ix = 0;
|
||||
buffer[ix++] = PoseMsg.Id;
|
||||
@ -53,59 +105,59 @@ namespace Passer.RoboidControl {
|
||||
LowLevelMessages.SendSpherical(buffer, ref ix, this.angularVelocity);
|
||||
return ix;
|
||||
}
|
||||
public override void Deserialize(byte[] buffer) {
|
||||
byte ix = 0;
|
||||
this.networkId = buffer[ix++];
|
||||
this.thingId = buffer[ix++];
|
||||
this.poseType = buffer[ix++];
|
||||
// public override void Deserialize(byte[] buffer) {
|
||||
// byte ix = 0;
|
||||
// this.networkId = buffer[ix++];
|
||||
// this.thingId = buffer[ix++];
|
||||
// this.poseType = buffer[ix++];
|
||||
|
||||
if ((poseType & Pose_Position) != 0)
|
||||
this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
if ((poseType & Pose_Orientation) != 0)
|
||||
this.orientation = LowLevelMessages.ReceiveSwingTwist(buffer, ref ix);
|
||||
if ((poseType & Pose_LinearVelocity) != 0)
|
||||
this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
if ((poseType & Pose_AngularVelocity) != 0)
|
||||
this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
}
|
||||
// if ((poseType & Pose_Position) != 0)
|
||||
// this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
// if ((poseType & Pose_Orientation) != 0)
|
||||
// this.orientation = LowLevelMessages.ReceiveSwingTwist(buffer, ref ix);
|
||||
// if ((poseType & Pose_LinearVelocity) != 0)
|
||||
// this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
// if ((poseType & Pose_AngularVelocity) != 0)
|
||||
// this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
|
||||
// }
|
||||
|
||||
public static bool Send(Participant client, byte thingId, Spherical position, SwingTwist orientation) {
|
||||
PoseMsg msg = new PoseMsg(client.networkId, thingId, position, orientation);
|
||||
return SendMsg(client, msg);
|
||||
}
|
||||
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
byte[] buffer = await Receive(dataStream, packetSize);
|
||||
PoseMsg msg = new PoseMsg(buffer);
|
||||
// public static bool Send(Participant client, byte thingId, Spherical position, SwingTwist orientation) {
|
||||
// PoseMsg msg = new PoseMsg(client.networkId, thingId, position, orientation);
|
||||
// return SendMsg(client, msg);
|
||||
// }
|
||||
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
// byte[] buffer = await Receive(dataStream, packetSize);
|
||||
// PoseMsg msg = new PoseMsg(buffer);
|
||||
|
||||
// Do no process poses with nwid == 0 (== local)
|
||||
if (msg.networkId == 0)
|
||||
return true;
|
||||
// // Do no process poses with nwid == 0 (== local)
|
||||
// if (msg.networkId == 0)
|
||||
// return true;
|
||||
|
||||
client.messageQueue.Enqueue(msg);
|
||||
return true;
|
||||
}
|
||||
// client.messageQueue.Enqueue(msg);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
public static bool SendTo(Participant participant, Thing thing) {
|
||||
if (participant == null || thing == null)
|
||||
return false;
|
||||
// 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;
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
// return participant.SendBuffer(ix);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -3,24 +3,59 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// Message for sending generic text.
|
||||
/// </summary>
|
||||
public class TextMsg : IMessage {
|
||||
public TextMsg(byte[] buffer) : base(buffer) {}
|
||||
/// <summary>
|
||||
/// The message ID
|
||||
/// </summary>
|
||||
public const byte Id = 0xB0;
|
||||
/// <summary>
|
||||
/// The length of the message without the text itself
|
||||
/// </summary>
|
||||
public const byte length = 2;
|
||||
/// <summary>
|
||||
/// The text
|
||||
/// </summary>
|
||||
public string text = "";
|
||||
|
||||
public override void Deserialize(byte[] buffer) {
|
||||
uint ix = 0;
|
||||
uint strlen = buffer[ix++];
|
||||
this.text = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, (int)strlen);
|
||||
/// <summary>
|
||||
/// Create a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="text">The text to send</param>
|
||||
public TextMsg(string text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
byte[] buffer = await Receive(dataStream, packetSize);
|
||||
TextMsg msg = new TextMsg(buffer);
|
||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||
public TextMsg(byte[] buffer) : base(buffer) { }
|
||||
|
||||
client.messageQueue.Enqueue(msg);
|
||||
return true;
|
||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||
public override byte Serialize(ref byte[] buffer) {
|
||||
if (buffer.Length < TextMsg.length + this.text.Length || this.text.Length == 0)
|
||||
return 0;
|
||||
|
||||
byte ix = 0;
|
||||
buffer[ix++] = TextMsg.Id;
|
||||
buffer[ix++] = (byte)this.text.Length;
|
||||
for (int textIx = 0; textIx < this.text.Length; textIx++)
|
||||
buffer[ix++] = (byte)this.text[textIx];
|
||||
return ix;
|
||||
}
|
||||
// public override void Deserialize(byte[] buffer) {
|
||||
// uint ix = 0;
|
||||
// uint strlen = buffer[ix++];
|
||||
// this.text = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, (int)strlen);
|
||||
// }
|
||||
|
||||
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
|
||||
// byte[] buffer = await Receive(dataStream, packetSize);
|
||||
// TextMsg msg = new TextMsg(buffer);
|
||||
|
||||
// client.messageQueue.Enqueue(msg);
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 711bdb0248c9f6848a6b4da15cc05db5
|
@ -318,7 +318,9 @@ namespace Passer.RoboidControl {
|
||||
if (client == this)
|
||||
continue;
|
||||
//UnityEngine.Debug.Log($"---> {client.ipAddress}");
|
||||
IMessage.SendMsg(client, msg);
|
||||
//IMessage.SendMsg(client, msg);
|
||||
msg.Serialize(ref client.buffer);
|
||||
client.SendBuffer(client.buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
11
README.md
11
README.md
@ -1,3 +1,10 @@
|
||||
\mainpage Control Core for C#
|
||||
\mainpage Roboid Control for C#
|
||||
|
||||
Control Core contains generic functionality for Controlling Things.
|
||||
Roboid Control support for C# applications.
|
||||
Includes support for the Unity game engine.
|
||||
|
||||
# Basic components
|
||||
|
||||
- Passer::RoboidControl::Thing
|
||||
- Passer::RoboidControl::Participant
|
||||
- Passer::RoboidControl::SiteServer
|
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5a7a42365df0d0459195576ad3bb50b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,22 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace Passer.RoboidControl.Core {
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// A sensor measuring the distance in the forward direction
|
||||
/// </summary>
|
||||
public class DistanceSensor : Thing {
|
||||
/// <summary>
|
||||
/// The current measured distance
|
||||
/// </summary>
|
||||
public float distance = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a new distance sensor
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for which the sensor is needed</param>
|
||||
public DistanceSensor(RemoteParticipant participant) : base(participant, true) { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a distance sensor with the given ID
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for with the sensor is needed</param>
|
||||
/// <param name="networkId">The network ID of the sensor</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
public DistanceSensor(RemoteParticipant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) {
|
||||
}
|
||||
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
/// @copydoc Passer::RoboidControl::Thing::CreateComponent
|
||||
public override void CreateComponent() {
|
||||
this.component = Unity.DistanceSensor.Create(this.parent);
|
||||
this.component.core = this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Function to extract the distance received in the binary message
|
||||
/// </summary>
|
||||
/// <param name="bytes">The byte array</param>
|
||||
public override void ProcessBinary(byte[] bytes) {
|
||||
byte ix = 0;
|
||||
this.distance = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
|
||||
|
@ -1,16 +1,32 @@
|
||||
using System;
|
||||
//using System;
|
||||
|
||||
namespace Passer.RoboidControl.Core {
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// A temperature sensor
|
||||
/// </summary>
|
||||
public class TemperatureSensor : Thing {
|
||||
public float temp = 0;
|
||||
/// <summary>
|
||||
/// The measured temperature
|
||||
/// </summary>
|
||||
public float temperature = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Create a temperature sensor with the given ID
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for with the sensor is needed</param>
|
||||
/// <param name="networkId">The network ID of the sensor</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
public TemperatureSensor(Participant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) {}
|
||||
|
||||
/// <summary>
|
||||
/// Function to extract the temperature received in the binary message
|
||||
/// </summary>
|
||||
/// <param name="bytes">The byte array</param>
|
||||
public override void ProcessBinary(byte[] bytes) {
|
||||
byte ix = 0;
|
||||
this.temp = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
|
||||
Console.WriteLine($"temperature {this.name} = {this.temp} C");
|
||||
this.temperature = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
|
||||
//Console.WriteLine($"temperature {this.name} = {this.temperature} C");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cd9e2534695ec844bd23b7a48ad498a
|
@ -1,12 +1,25 @@
|
||||
namespace Passer.RoboidControl {
|
||||
|
||||
/// <summary>
|
||||
/// A sensor which can detect touches
|
||||
/// </summary>
|
||||
public class TouchSensor : Thing {
|
||||
/// <summary>
|
||||
/// Value which is true when the sensor is touching something, false otherwise
|
||||
/// </summary>
|
||||
public bool touchedSomething = false;
|
||||
|
||||
/// <summary>
|
||||
/// Create a touch sensor
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for with the sensor is needed</param>
|
||||
/// <param name="invokeEvent">True when the creation should trigger an event</param>
|
||||
public TouchSensor(RemoteParticipant participant, bool invokeEvent = true) : base(participant, invokeEvent) {
|
||||
touchedSomething = false;
|
||||
}
|
||||
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
/// @copydoc Passer::RoboidControl::Thing::CreateComponent
|
||||
public override void CreateComponent() {
|
||||
this.component = Unity.TouchSensor.Create(this);
|
||||
this.component.core = this;
|
||||
|
153
Thing.cs
153
Thing.cs
@ -31,21 +31,41 @@ namespace Passer.RoboidControl {
|
||||
Humanoid,
|
||||
ExternalSensor
|
||||
};
|
||||
/// <summary>
|
||||
/// The type of this thing. This can be either a Thing::Type (needs casting)
|
||||
/// or a byte value for custom types.
|
||||
/// </summary>
|
||||
public byte type;
|
||||
|
||||
#endregion Types
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The participant to which this thing belongs
|
||||
/// </summary>
|
||||
public RemoteParticipant participant;
|
||||
|
||||
public delegate void ChangeHandler();
|
||||
public delegate void SphericalHandler(Spherical v);
|
||||
|
||||
/// <summary>
|
||||
/// The network ID of this thing.
|
||||
/// </summary>
|
||||
public byte networkId;
|
||||
/// <summary>
|
||||
/// The ID of this thing
|
||||
/// </summary>
|
||||
public byte id;
|
||||
|
||||
public event ChangeHandler OnParentChanged = delegate {};
|
||||
/// <summary>
|
||||
/// Event which is triggered when the parent changes
|
||||
/// </summary>
|
||||
public event ChangeHandler OnParentChanged = delegate { };
|
||||
private Thing _parent;
|
||||
/// <summary>
|
||||
/// The parent of this thing
|
||||
/// </summary>
|
||||
public Thing parent {
|
||||
get => _parent;
|
||||
set {
|
||||
@ -63,22 +83,38 @@ namespace Passer.RoboidControl {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attach a thing as a child of this thing
|
||||
/// </summary>
|
||||
/// <param name="child">The thing to attach as a child</param>
|
||||
public void AddChild(Thing child) {
|
||||
if (children.Find(thing => thing == child) != null)
|
||||
return;
|
||||
child._parent = this;
|
||||
children.Add(child);
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove the given thing as a child of this thing
|
||||
/// </summary>
|
||||
/// <param name="child"></param>
|
||||
public void RemoveChild(Thing child) {
|
||||
children.Remove(child);
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
/// <summary>
|
||||
/// The list of children of this thing
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public List<Thing> children = new List<Thing>();
|
||||
public byte type;
|
||||
|
||||
public event ChangeHandler OnNameChanged = delegate {};
|
||||
/// <summary>
|
||||
/// Event which is triggered when the name changes
|
||||
/// </summary>
|
||||
public event ChangeHandler OnNameChanged = delegate { };
|
||||
private string _name = "";
|
||||
/// <summary>
|
||||
/// The name of the thing
|
||||
/// </summary>
|
||||
public virtual string name {
|
||||
get => _name;
|
||||
set {
|
||||
@ -89,11 +125,19 @@ namespace Passer.RoboidControl {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An URL pointing to the location where a model of the thing can be found
|
||||
/// </summary>
|
||||
public string modelUrl = "";
|
||||
|
||||
public byte poseUpdated = 0x00;
|
||||
public event ChangeHandler OnPositionChanged = delegate {};
|
||||
/// <summary>
|
||||
/// Event triggered when the position has changed
|
||||
/// </summary>
|
||||
public event ChangeHandler OnPositionChanged = delegate { };
|
||||
private Spherical _position = Spherical.zero;
|
||||
/// <summary>
|
||||
/// The position of the thing in local space, in meters.
|
||||
/// </summary>
|
||||
public Spherical position {
|
||||
get { return _position; }
|
||||
set {
|
||||
@ -104,8 +148,14 @@ namespace Passer.RoboidControl {
|
||||
}
|
||||
}
|
||||
|
||||
public event ChangeHandler OnOrientationChanged = delegate {};
|
||||
/// <summary>
|
||||
/// Event triggered when the orientation has changed
|
||||
/// </summary>
|
||||
public event ChangeHandler OnOrientationChanged = delegate { };
|
||||
private SwingTwist _orientation = SwingTwist.zero;
|
||||
/// <summary>
|
||||
/// The orientation of the thing in local space
|
||||
/// </summary>
|
||||
public SwingTwist orientation {
|
||||
get { return _orientation; }
|
||||
set {
|
||||
@ -116,8 +166,14 @@ namespace Passer.RoboidControl {
|
||||
}
|
||||
}
|
||||
|
||||
public event SphericalHandler OnLinearVelocityChanged = delegate {};
|
||||
/// <summary>
|
||||
/// Event triggered when the linear velocity has changed
|
||||
/// </summary>
|
||||
public event SphericalHandler OnLinearVelocityChanged = delegate { };
|
||||
private Spherical _linearVelocity = Spherical.zero;
|
||||
/// <summary>
|
||||
/// The linear velocity of the thing in local space in meters per second
|
||||
/// </summary>
|
||||
public Spherical linearVelocity {
|
||||
get => _linearVelocity;
|
||||
set {
|
||||
@ -127,9 +183,15 @@ namespace Passer.RoboidControl {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The angular velocity of the thing in local space
|
||||
/// </summary>
|
||||
public Spherical angularVelocity = Spherical.zero;
|
||||
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
/// <summary>
|
||||
/// A reference to the representation of the thing in Unity
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public Unity.Thing component = null;
|
||||
#endif
|
||||
@ -138,67 +200,92 @@ namespace Passer.RoboidControl {
|
||||
|
||||
#region Init
|
||||
|
||||
/// <summary>
|
||||
/// Create a new thing for the given participant
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for which this thing is created</param>
|
||||
/// <param name="invokeEvent">True when a new thing event should be triggered</param>
|
||||
public Thing(RemoteParticipant participant, bool invokeEvent = false) {
|
||||
this.participant = participant;
|
||||
if (invokeEvent)
|
||||
InvokeNewThing(this);
|
||||
}
|
||||
public Thing(RemoteParticipant sender, byte networkId, byte thingId, byte thingType = 0) {
|
||||
this.participant = sender;
|
||||
/// <summary>
|
||||
/// Create a new thing for the given participant
|
||||
/// </summary>
|
||||
/// <param name="participant">The participant for which this thing is created</param>
|
||||
/// <param name="networkId">The network ID of the thing</param>
|
||||
/// <param name="thingId">The ID of the thing</param>
|
||||
/// <param name="thingType">The type of thing</param>
|
||||
public Thing(RemoteParticipant participant, byte networkId, byte thingId, byte thingType = 0) {
|
||||
this.participant = participant;
|
||||
this.id = thingId;
|
||||
this.type = thingType;
|
||||
this.networkId = networkId;
|
||||
}
|
||||
|
||||
public virtual void CreateComponent() {}
|
||||
/// <summary>
|
||||
/// Function which can be used to create components in external engines.
|
||||
/// </summary>
|
||||
/// Currently this is used to create GameObjects in Unity
|
||||
public virtual void CreateComponent() { }
|
||||
|
||||
#endregion Init
|
||||
|
||||
#region Update
|
||||
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
/// <summary>
|
||||
/// Convience function for use in Unity which removes the need for a currentTime argument
|
||||
/// </summary>
|
||||
public void Update() {
|
||||
Update((ulong)UnityEngine.Time.time * 1000);
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Update this thing
|
||||
/// </summary>
|
||||
/// <param name="currentTime">The current time in milliseconds</param>
|
||||
public virtual void Update(ulong currentTime) {
|
||||
// should recurse over children...
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function used to generate binary data for this thing
|
||||
/// </summary>
|
||||
/// <returns>a byte array with the binary data</returns>
|
||||
/// @sa Passer::RoboidControl::BinaryMsg
|
||||
public virtual byte[] GenerateBinary() { return new byte[0]; }
|
||||
|
||||
/// <summary>
|
||||
/// Function used to process binary data received for this thing
|
||||
/// </summary>
|
||||
/// <param name="bytes">The binary data</param>
|
||||
public virtual void ProcessBinary(byte[] bytes) {
|
||||
//if (sensor != null)
|
||||
// sensor.ProcessBytes(bytes);
|
||||
}
|
||||
|
||||
#endregion Update
|
||||
|
||||
// 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 = delegate {};
|
||||
/// <summary>
|
||||
/// Event triggered when a new thing has been created
|
||||
/// </summary>
|
||||
public static event ThingHandler OnNewThing = delegate { };
|
||||
/// <summary>
|
||||
/// Trigger the creation for the given thing
|
||||
/// </summary>
|
||||
/// <param name="thing">The created thing</param>
|
||||
public static void InvokeNewThing(Thing thing) {
|
||||
OnNewThing?.Invoke(thing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the thing has the given properaties
|
||||
/// </summary>
|
||||
/// <param name="thing">The thing to check</param>
|
||||
/// <param name="networkId">The network ID to compare to</param>
|
||||
/// <param name="thingId">The thing ID to compare to</param>
|
||||
/// <returns>True when the thing has the given properties</returns>
|
||||
public static bool IsThing(Thing thing, byte networkId, byte thingId) {
|
||||
if (thing == null)
|
||||
return false;
|
||||
|
@ -6,15 +6,15 @@ namespace Passer.RoboidControl.Unity {
|
||||
|
||||
public class DistanceSensor : Thing {
|
||||
|
||||
public new Core.DistanceSensor core {
|
||||
get => (Core.DistanceSensor)base.core;
|
||||
public new RoboidControl.DistanceSensor core {
|
||||
get => (RoboidControl.DistanceSensor)base.core;
|
||||
set => base.core = value;
|
||||
}
|
||||
|
||||
protected virtual void Start() {
|
||||
if (core == null) {
|
||||
SiteServer siteServer = FindAnyObjectByType<SiteServer>();
|
||||
SetCoreThing(new Core.DistanceSensor(siteServer.site));
|
||||
SetCoreThing(new RoboidControl.DistanceSensor(siteServer.site));
|
||||
}
|
||||
|
||||
StartCoroutine(MeasureDistance());
|
||||
|
@ -3,11 +3,20 @@ using UnityEngine;
|
||||
|
||||
namespace Passer.RoboidControl.Unity {
|
||||
|
||||
/// <summary>
|
||||
/// The representation of a Thing in Unity
|
||||
/// </summary>
|
||||
public class Thing : MonoBehaviour {
|
||||
|
||||
/// <summary>
|
||||
/// The core C# thing
|
||||
/// </summary>
|
||||
[field: SerializeField]
|
||||
public RoboidControl.Thing core {get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the core C# thing
|
||||
/// </summary>
|
||||
protected void SetCoreThing(RoboidControl.Thing thing) {
|
||||
core = thing;
|
||||
core.component = this;
|
||||
@ -20,6 +29,9 @@ namespace Passer.RoboidControl.Unity {
|
||||
siteServer.site.Add(thing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the Unity representation
|
||||
/// </summary>
|
||||
protected virtual void Update() {
|
||||
if (core == null)
|
||||
return;
|
||||
|
@ -3,6 +3,9 @@ using UnityEngine;
|
||||
|
||||
namespace Passer.RoboidControl.Unity {
|
||||
|
||||
/// <summary>
|
||||
/// The Unity representation of the TouchSensor
|
||||
/// </summary>
|
||||
public class TouchSensor : Thing {
|
||||
|
||||
public RoboidControl.TouchSensor coreSensor {
|
||||
|
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5d87461365fd8a4da528aa84a49e62c
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 743b128a79ef8414fa29d7bb3b9e2ac8
|
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92729868a8379c04197dcb80d0276a63
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
x
Reference in New Issue
Block a user