safety snapshot: recover geo importers and current quest/server state
This commit is contained in:
198
Assets/Scripts/Networking/Shared/SweBoundaryManifest.cs
Normal file
198
Assets/Scripts/Networking/Shared/SweBoundaryManifest.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FloodSWE.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class SweBoundaryManifest
|
||||
{
|
||||
public int schema_version;
|
||||
public SweBoundarySource[] sources;
|
||||
public SweBoundarySink[] sinks;
|
||||
public SweBoundaryTile[] tiles;
|
||||
|
||||
[NonSerialized] private Dictionary<string, SweBoundaryTile> tileLookup;
|
||||
[NonSerialized] private Dictionary<int, SweBoundarySource> sourceLookup;
|
||||
[NonSerialized] private Dictionary<int, SweBoundarySink> sinkLookup;
|
||||
|
||||
public static bool TryLoad(string json, out SweBoundaryManifest manifest)
|
||||
{
|
||||
manifest = null;
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
manifest = JsonUtility.FromJson<SweBoundaryManifest>(json);
|
||||
if (manifest == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
manifest.BuildLookup();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"SweBoundaryManifest: failed to parse manifest. {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetTile(string lod, int tileX, int tileY, out SweBoundaryTile tile)
|
||||
{
|
||||
tile = null;
|
||||
if (tileLookup == null)
|
||||
{
|
||||
BuildLookup();
|
||||
}
|
||||
|
||||
return tileLookup != null && tileLookup.TryGetValue(MakeTileKey(lod, tileX, tileY), out tile);
|
||||
}
|
||||
|
||||
public bool TryGetSource(int id, out SweBoundarySource source)
|
||||
{
|
||||
source = null;
|
||||
if (sourceLookup == null)
|
||||
{
|
||||
BuildLookup();
|
||||
}
|
||||
|
||||
return sourceLookup != null && sourceLookup.TryGetValue(id, out source);
|
||||
}
|
||||
|
||||
public bool TryGetSink(int id, out SweBoundarySink sink)
|
||||
{
|
||||
sink = null;
|
||||
if (sinkLookup == null)
|
||||
{
|
||||
BuildLookup();
|
||||
}
|
||||
|
||||
return sinkLookup != null && sinkLookup.TryGetValue(id, out sink);
|
||||
}
|
||||
|
||||
public static int ParseLod(string lod)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lod))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
string value = lod.Trim().ToLowerInvariant();
|
||||
if (value.StartsWith("lod", StringComparison.Ordinal))
|
||||
{
|
||||
value = value.Substring(3);
|
||||
}
|
||||
|
||||
return int.TryParse(value, out int parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
private void BuildLookup()
|
||||
{
|
||||
tileLookup = new Dictionary<string, SweBoundaryTile>(StringComparer.OrdinalIgnoreCase);
|
||||
sourceLookup = new Dictionary<int, SweBoundarySource>();
|
||||
sinkLookup = new Dictionary<int, SweBoundarySink>();
|
||||
if (tiles == null)
|
||||
{
|
||||
tileLookup = tileLookup ?? new Dictionary<string, SweBoundaryTile>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (tiles != null)
|
||||
{
|
||||
for (int i = 0; i < tiles.Length; i++)
|
||||
{
|
||||
SweBoundaryTile tile = tiles[i];
|
||||
if (tile == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tileLookup[MakeTileKey(tile.lod, tile.tile_x, tile.tile_y)] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
if (sources != null)
|
||||
{
|
||||
for (int i = 0; i < sources.Length; i++)
|
||||
{
|
||||
SweBoundarySource source = sources[i];
|
||||
if (source != null)
|
||||
{
|
||||
sourceLookup[source.id] = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sinks != null)
|
||||
{
|
||||
for (int i = 0; i < sinks.Length; i++)
|
||||
{
|
||||
SweBoundarySink sink = sinks[i];
|
||||
if (sink != null)
|
||||
{
|
||||
sinkLookup[sink.id] = sink;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string MakeTileKey(string lod, int tileX, int tileY)
|
||||
{
|
||||
return $"{lod}|{tileX}|{tileY}";
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class SweBoundarySource
|
||||
{
|
||||
public int id;
|
||||
public int tile_count;
|
||||
public int total_pixels;
|
||||
public SweBoundaryParams @params;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class SweBoundarySink
|
||||
{
|
||||
public int id;
|
||||
public int tile_count;
|
||||
public int total_pixels;
|
||||
public SweBoundaryParams @params;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class SweBoundaryTile
|
||||
{
|
||||
public string lod;
|
||||
public int tile_x;
|
||||
public int tile_y;
|
||||
public SweBoundaryTileIdRef[] source_ids;
|
||||
public SweBoundaryTileIdRef[] sink_ids;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class SweBoundaryParams
|
||||
{
|
||||
public string name;
|
||||
public string mode;
|
||||
public float trigger_level_m;
|
||||
public float max_outflow_m3s;
|
||||
|
||||
public bool IsMode(string value)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(mode) &&
|
||||
string.Equals(mode.Trim(), value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class SweBoundaryTileIdRef
|
||||
{
|
||||
public int id;
|
||||
public int pixels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b866d23532dcb9554b203749a3b654c9
|
||||
178
Assets/Scripts/Networking/Shared/SweUdpProtocol.cs
Normal file
178
Assets/Scripts/Networking/Shared/SweUdpProtocol.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Networking/Shared/SweUdpProtocol.cs.meta
Normal file
2
Assets/Scripts/Networking/Shared/SweUdpProtocol.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70f5464ad7c6d1f288fa573272c0ed49
|
||||
Reference in New Issue
Block a user