fec2cd8d24
- serialization - chunks - parralelfor test
189 lines
7.5 KiB
C#
189 lines
7.5 KiB
C#
using System.Numerics;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace Nerfed.Runtime.Scene;
|
|
|
|
/// <summary>
|
|
/// Human-readable JSON scene serializer.
|
|
///
|
|
/// Example output:
|
|
/// <code>
|
|
/// {
|
|
/// "version": 1,
|
|
/// "name": "MyScene",
|
|
/// "entities": [
|
|
/// {
|
|
/// "id": "a1b2c3d4-...",
|
|
/// "tag": "Player",
|
|
/// "parentId": null,
|
|
/// "components": [
|
|
/// {
|
|
/// "type": "Nerfed.Runtime.Components.LocalTransform",
|
|
/// "data": {
|
|
/// "position": { "x": 0.0, "y": 0.0, "z": 0.0 },
|
|
/// "rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
|
|
/// "scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
|
|
/// }
|
|
/// }
|
|
/// ]
|
|
/// }
|
|
/// ],
|
|
/// "relations": [
|
|
/// {
|
|
/// "type": "Nerfed.Runtime.Components.OwnerRelation",
|
|
/// "entityA": "a1b2c3d4-...",
|
|
/// "entityB": "e5f6a7b8-...",
|
|
/// "data": {}
|
|
/// }
|
|
/// ]
|
|
/// }
|
|
/// </code>
|
|
/// </summary>
|
|
public sealed class JsonSceneSerializer : ISceneSerializer
|
|
{
|
|
private static readonly JsonSerializerOptions Options = new() {
|
|
WriteIndented = true,
|
|
Converters =
|
|
{
|
|
new Vector3JsonConverter(),
|
|
new QuaternionJsonConverter(),
|
|
new SceneComponentDataJsonConverter(),
|
|
new SceneRelationDataJsonConverter(),
|
|
},
|
|
};
|
|
|
|
public void Serialize(SceneData scene, Stream stream) {
|
|
JsonSerializer.Serialize(stream, scene, Options);
|
|
}
|
|
|
|
public SceneData Deserialize(Stream stream) {
|
|
return JsonSerializer.Deserialize<SceneData>(stream, Options)
|
|
?? throw new InvalidOperationException("Failed to deserialize scene: root element was null.");
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Converters
|
|
// -------------------------------------------------------------------------
|
|
|
|
private sealed class Vector3JsonConverter : JsonConverter<Vector3>
|
|
{
|
|
public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
float x = 0f, y = 0f, z = 0f;
|
|
reader.Read(); // StartObject
|
|
while(reader.Read() && reader.TokenType != JsonTokenType.EndObject) {
|
|
string name = reader.GetString()!;
|
|
reader.Read();
|
|
switch(name) {
|
|
case "x": x = reader.GetSingle(); break;
|
|
case "y": y = reader.GetSingle(); break;
|
|
case "z": z = reader.GetSingle(); break;
|
|
}
|
|
}
|
|
return new Vector3(x, y, z);
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) {
|
|
writer.WriteStartObject();
|
|
writer.WriteNumber("x", value.X);
|
|
writer.WriteNumber("y", value.Y);
|
|
writer.WriteNumber("z", value.Z);
|
|
writer.WriteEndObject();
|
|
}
|
|
}
|
|
|
|
private sealed class QuaternionJsonConverter : JsonConverter<Quaternion>
|
|
{
|
|
public override Quaternion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
float x = 0f, y = 0f, z = 0f, w = 1f;
|
|
reader.Read(); // StartObject
|
|
while(reader.Read() && reader.TokenType != JsonTokenType.EndObject) {
|
|
string name = reader.GetString()!;
|
|
reader.Read();
|
|
switch(name) {
|
|
case "x": x = reader.GetSingle(); break;
|
|
case "y": y = reader.GetSingle(); break;
|
|
case "z": z = reader.GetSingle(); break;
|
|
case "w": w = reader.GetSingle(); break;
|
|
}
|
|
}
|
|
return new Quaternion(x, y, z, w);
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, Quaternion value, JsonSerializerOptions options) {
|
|
writer.WriteStartObject();
|
|
writer.WriteNumber("x", value.X);
|
|
writer.WriteNumber("y", value.Y);
|
|
writer.WriteNumber("z", value.Z);
|
|
writer.WriteNumber("w", value.W);
|
|
writer.WriteEndObject();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Buffers the full JSON object, resolves the CLR component type from the "type" field,
|
|
/// then deserializes "data" using that concrete type.
|
|
/// </summary>
|
|
private sealed class SceneComponentDataJsonConverter : JsonConverter<SceneComponentData>
|
|
{
|
|
public override SceneComponentData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
using JsonDocument doc = JsonDocument.ParseValue(ref reader);
|
|
JsonElement root = doc.RootElement;
|
|
|
|
string typeName = root.GetProperty("type").GetString()
|
|
?? throw new JsonException("Missing or null 'type' field in component data.");
|
|
|
|
Type componentType = SceneManager.GetComponentType(typeName)
|
|
?? throw new JsonException($"Unknown component type '{typeName}'. Ensure the struct is marked with [SceneComponent].");
|
|
|
|
string rawData = root.GetProperty("data").GetRawText();
|
|
ValueType value = (ValueType)JsonSerializer.Deserialize(rawData, componentType, options)!;
|
|
|
|
return new SceneComponentData { Type = typeName, Value = value };
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, SceneComponentData value, JsonSerializerOptions options) {
|
|
writer.WriteStartObject();
|
|
writer.WriteString("type", value.Type);
|
|
writer.WritePropertyName("data");
|
|
JsonSerializer.Serialize(writer, value.Value, value.Value.GetType(), options);
|
|
writer.WriteEndObject();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Same pattern as <see cref="SceneComponentDataJsonConverter"/> but for relation data.
|
|
/// Resolves the type via <see cref="SceneManager.GetRelationType"/>.
|
|
/// </summary>
|
|
private sealed class SceneRelationDataJsonConverter : JsonConverter<SceneRelationData>
|
|
{
|
|
public override SceneRelationData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
using JsonDocument doc = JsonDocument.ParseValue(ref reader);
|
|
JsonElement root = doc.RootElement;
|
|
|
|
string typeName = root.GetProperty("type").GetString()
|
|
?? throw new JsonException("Missing or null 'type' field in relation data.");
|
|
|
|
Type relationType = SceneManager.GetRelationType(typeName)
|
|
?? throw new JsonException($"Unknown relation type '{typeName}'. Ensure the struct is marked with [SceneRelation].");
|
|
|
|
Guid entityA = root.GetProperty("entityA").GetGuid();
|
|
Guid entityB = root.GetProperty("entityB").GetGuid();
|
|
|
|
string rawData = root.GetProperty("data").GetRawText();
|
|
ValueType value = (ValueType)JsonSerializer.Deserialize(rawData, relationType, options)!;
|
|
|
|
return new SceneRelationData { Type = typeName, EntityA = entityA, EntityB = entityB, Value = value };
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, SceneRelationData value, JsonSerializerOptions options) {
|
|
writer.WriteStartObject();
|
|
writer.WriteString("type", value.Type);
|
|
writer.WriteString("entityA", value.EntityA);
|
|
writer.WriteString("entityB", value.EntityB);
|
|
writer.WritePropertyName("data");
|
|
JsonSerializer.Serialize(writer, value.Value, value.Value.GetType(), options);
|
|
writer.WriteEndObject();
|
|
}
|
|
}
|
|
} |