Files
DTrierFlood_New/Assets/Scripts/Networking/Shared/SweUdpProtocol.cs

258 lines
7.7 KiB
C#

using System;
using System.IO;
using System.Text;
using FloodSWE.Streaming;
using FloodSWE.TileGraph;
using UnityEngine;
namespace FloodSWE.Networking
{
public static class SweUdpProtocol
{
public const byte FrameType = 1;
public const byte ControlType = 2;
public const byte AckType = 3;
public const byte StateType = 4;
public static byte[] EncodeFrame(HeightmapPacket packet)
{
using (var stream = new MemoryStream(64 + (packet.Payload?.Length ?? 0)))
using (var writer = new BinaryWriter(stream, Encoding.UTF8, true))
{
writer.Write(FrameType);
writer.Write(packet.FrameId);
writer.Write(packet.Tile.Lod);
writer.Write(packet.Tile.X);
writer.Write(packet.Tile.Y);
writer.Write(packet.Resolution);
writer.Write(packet.MinHeight);
writer.Write(packet.MaxHeight);
int length = packet.Payload != null ? packet.Payload.Length : 0;
writer.Write(length);
if (length > 0)
{
writer.Write(packet.Payload);
}
writer.Flush();
return stream.ToArray();
}
}
public static bool TryDecodeFrame(byte[] data, out HeightmapPacket packet)
{
packet = default;
if (data == null || data.Length < 32)
{
return false;
}
try
{
using (var stream = new MemoryStream(data))
using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
{
if (reader.ReadByte() != FrameType)
{
return false;
}
int frameId = reader.ReadInt32();
int lod = reader.ReadInt32();
int x = reader.ReadInt32();
int y = reader.ReadInt32();
int resolution = reader.ReadInt32();
float minHeight = reader.ReadSingle();
float maxHeight = reader.ReadSingle();
int payloadLen = reader.ReadInt32();
if (payloadLen <= 0 || payloadLen > data.Length)
{
return false;
}
byte[] payload = reader.ReadBytes(payloadLen);
if (payload.Length != payloadLen)
{
return false;
}
packet = new HeightmapPacket
{
FrameId = frameId,
Tile = new TileId(lod, x, y),
Resolution = resolution,
MinHeight = minHeight,
MaxHeight = maxHeight,
Payload = payload,
};
return packet.IsValid;
}
}
catch
{
return false;
}
}
public static byte[] EncodeControl(SweControlCommand command)
{
string json = JsonUtility.ToJson(command);
byte[] utf8 = Encoding.UTF8.GetBytes(json);
byte[] payload = new byte[utf8.Length + 1];
payload[0] = ControlType;
Buffer.BlockCopy(utf8, 0, payload, 1, utf8.Length);
return payload;
}
public static bool TryDecodeControl(byte[] data, out SweControlCommand command)
{
command = null;
if (data == null || data.Length < 2 || data[0] != ControlType)
{
return false;
}
try
{
string json = Encoding.UTF8.GetString(data, 1, data.Length - 1);
command = JsonUtility.FromJson<SweControlCommand>(json);
return command != null;
}
catch
{
return false;
}
}
public static byte[] EncodeAck(string message)
{
byte[] utf8 = Encoding.UTF8.GetBytes(message ?? string.Empty);
byte[] payload = new byte[utf8.Length + 1];
payload[0] = AckType;
Buffer.BlockCopy(utf8, 0, payload, 1, utf8.Length);
return payload;
}
public static bool TryDecodeAck(byte[] data, out string message)
{
message = null;
if (data == null || data.Length < 1 || data[0] != AckType)
{
return false;
}
try
{
message = data.Length == 1
? string.Empty
: Encoding.UTF8.GetString(data, 1, data.Length - 1);
return true;
}
catch
{
message = null;
return false;
}
}
public static byte[] EncodeState(SweBoundaryStateMessage message)
{
string json = JsonUtility.ToJson(message);
byte[] utf8 = Encoding.UTF8.GetBytes(json);
byte[] payload = new byte[utf8.Length + 1];
payload[0] = StateType;
Buffer.BlockCopy(utf8, 0, payload, 1, utf8.Length);
return payload;
}
public static bool TryDecodeState(byte[] data, out SweBoundaryStateMessage message)
{
message = null;
if (data == null || data.Length < 2 || data[0] != StateType)
{
return false;
}
try
{
string json = Encoding.UTF8.GetString(data, 1, data.Length - 1);
message = JsonUtility.FromJson<SweBoundaryStateMessage>(json);
return message != null;
}
catch
{
return false;
}
}
}
[Serializable]
public sealed class SweControlCommand
{
public string command;
public string lod;
public int tileX;
public int tileY;
public int sourceId;
public float sourceLevel;
public int sinkId;
public float sinkLevel;
public string checkpoint;
public float u;
public float v;
public float radius;
public float porosity;
public string boundaryKind;
public int boundaryId;
public int enabled;
public float waterLevelM;
public float velocityUMps;
public float velocityVMps;
public float depthRateMps;
public bool replaceAll;
public bool subscribe;
public SweBoundaryProfile[] boundaries;
}
[Serializable]
public sealed class SweBoundaryProfile
{
public string boundaryKind;
public int boundaryId;
public bool enabled;
public float waterLevelM;
public float velocityUMps;
public float velocityVMps;
public float depthRateMps;
public SweBoundaryProfile Clone()
{
return new SweBoundaryProfile
{
boundaryKind = boundaryKind,
boundaryId = boundaryId,
enabled = enabled,
waterLevelM = waterLevelM,
velocityUMps = velocityUMps,
velocityVMps = velocityVMps,
depthRateMps = depthRateMps,
};
}
}
[Serializable]
public sealed class SweBoundaryStateMessage
{
public string messageType;
public int schemaVersion;
public string lod;
public int tileX;
public int tileY;
public SweBoundaryProfile[] boundaries;
}
}