using System; using System.Buffers; using System.IO; using GameCult.Caching; using GameCult.Mesh; using MessagePack; #nullable enable namespace GameCult.Aetheria.State.Verse { internal static class AetheriaRuntimeCultCacheDocumentStore { public static void WriteEveCommand(string path, AetheriaRuntimeEveCommandDocument document) { WriteDocument( path, $"command:gamecult.eve.command.{document.CommandId}.v1", AetheriaRuntimeEveCommandDocument.SchemaId, "gamecult.eve.command", "gamecult.eve.command.v1", document.IssuedAtUtc, MessagePackSerializer.Serialize(document)); } public static AetheriaRuntimeEveCommandDocument ReadEveCommand(string path) { return MessagePackSerializer.Deserialize( CultMesh.ReadSingleFileDocumentPayload(path, AetheriaRuntimeEveCommandDocument.SchemaId)); } public static void WriteDaemonCommand(string path, AetheriaRuntimeDaemonCommandDocument document) { WriteDocument( path, $"command:gamecult.aetheria.daemon_command.{document.CommandId}.v1", AetheriaRuntimeDaemonSchemas.Command, "gamecult.aetheria.daemon_command", "gamecult.aetheria.daemon_command.v1", document.IssuedAtUtc, MessagePackSerializer.Serialize(document)); } public static AetheriaRuntimeDaemonCommandDocument ReadDaemonCommand(string path) { return MessagePackSerializer.Deserialize( CultMesh.ReadSingleFileDocumentPayload(path, AetheriaRuntimeDaemonSchemas.Command)); } private static void WriteDocument( string path, string key, string schemaId, string schemaName, string schemaVersion, string storedAt, byte[] payload) { CultMesh.WriteSingleFileDocumentPayload( path, new CultRecordKey(key), new CultMeshSingleFileDocumentSchema(schemaId, schemaName, schemaVersion), storedAt, payload); } internal static byte[] WriteRuntimeSurfaceDocument(AetheriaRuntimeSurfaceDocument document) { var buffer = new ArrayBufferWriter(); var writer = new MessagePackWriter(buffer); writer.WriteArrayHeader(7); writer.Write(document.ProviderId ?? ""); writer.Write(document.ProviderKind ?? ""); writer.Write(document.Title ?? ""); writer.Write(document.Version); writer.Write(document.UpdatedAtUtc ?? ""); WriteSurfaceTree(ref writer, document.Surface); WriteSurfaceCommands(ref writer, document.Commands); writer.Flush(); return buffer.WrittenSpan.ToArray(); } private static void WriteSurfaceTree(ref MessagePackWriter writer, AetheriaRuntimeSurfaceTree tree) { tree ??= new AetheriaRuntimeSurfaceTree( "", new AetheriaRuntimeSurfaceComponent( "", "surface", new System.Collections.Generic.Dictionary(StringComparer.Ordinal), Array.Empty()), Array.Empty()); writer.WriteArrayHeader(3); writer.Write(tree.Id ?? ""); WriteSurfaceComponent(ref writer, tree.Root); writer.WriteArrayHeader(tree.Styles?.Count ?? 0); foreach (var style in tree.Styles ?? Array.Empty()) { writer.WriteArrayHeader(2); writer.Write(style.Name ?? ""); writer.Write(style.Value ?? ""); } } private static void WriteSurfaceComponent(ref MessagePackWriter writer, AetheriaRuntimeSurfaceComponent component) { component ??= new AetheriaRuntimeSurfaceComponent( "", "", new System.Collections.Generic.Dictionary(StringComparer.Ordinal), Array.Empty()); writer.WriteArrayHeader(8); writer.Write(component.Id ?? ""); writer.Write(component.Kind ?? ""); writer.WriteMapHeader(component.Props?.Count ?? 0); foreach (var prop in component.Props ?? new System.Collections.Generic.Dictionary(StringComparer.Ordinal)) { writer.Write(prop.Key ?? ""); writer.Write(prop.Value ?? ""); } writer.WriteArrayHeader(component.Children?.Count ?? 0); foreach (var child in component.Children ?? Array.Empty()) { WriteSurfaceComponent(ref writer, child); } writer.WriteArrayHeader(component.StateBindings?.Count ?? 0); foreach (var binding in component.StateBindings ?? Array.Empty()) { var record = CultMesh.StateBindingRecord(binding); writer.WriteArrayHeader(6); writer.Write(record.TargetProp); writer.Write(record.PointerId); writer.Write(record.SourceId); writer.Write(record.SchemaId); writer.Write(record.RouteKind); writer.Write(record.RouteDescription); } writer.WriteArrayHeader(component.EmbeddedDocuments?.Count ?? 0); foreach (var slot in component.EmbeddedDocuments ?? Array.Empty()) { var route = slot.RouteHint ?? CultMeshRouteHint.Automatic; writer.WriteArrayHeader(6); writer.Write(slot.SlotId ?? ""); writer.Write(slot.DocumentId ?? ""); writer.Write(slot.SchemaId ?? ""); writer.Write(slot.PresentationKind ?? ""); writer.Write(route.Kind.ToString()); writer.Write(route.Description ?? ""); } WriteStringMap(ref writer, component.Layout); WriteStringMap(ref writer, component.Style); } private static void WriteSurfaceCommands( ref MessagePackWriter writer, System.Collections.Generic.IReadOnlyList commands) { writer.WriteArrayHeader(commands?.Count ?? 0); foreach (var command in commands ?? Array.Empty()) { var record = CultMesh.OperationBindingRecord(command.Operation); writer.WriteArrayHeader(5); writer.Write(record.OperationId); writer.Write(record.Label); writer.Write(record.SchemaId); writer.Write(record.RouteKind); writer.Write(record.RouteDescription); } } private static void WriteStringMap( ref MessagePackWriter writer, System.Collections.Generic.IReadOnlyDictionary values) { writer.WriteMapHeader(values?.Count ?? 0); foreach (var value in values ?? new System.Collections.Generic.Dictionary(StringComparer.Ordinal)) { writer.Write(value.Key ?? ""); writer.Write(value.Value ?? ""); } } internal static AetheriaRuntimeSurfaceDocument ReadRuntimeSurfaceDocument(byte[] payload) { var reader = new MessagePackReader(payload); var fields = reader.ReadArrayHeader(); var providerId = ReadFieldString(ref reader, fields, 0, ""); var providerKind = ReadFieldString(ref reader, fields, 1, ""); var title = ReadFieldString(ref reader, fields, 2, ""); var version = ReadFieldInt64(ref reader, fields, 3, 0); var updatedAtUtc = ReadFieldString(ref reader, fields, 4, ""); var surface = ReadFieldSurfaceTree(ref reader, fields, 5); var commands = ReadFieldSurfaceCommands(ref reader, fields, 6); for (var index = 7; index < fields; index++) { reader.Skip(); } return new AetheriaRuntimeSurfaceDocument( providerId, providerKind, title, version, updatedAtUtc, surface, commands); } private static AetheriaRuntimeSurfaceTree ReadFieldSurfaceTree( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return new AetheriaRuntimeSurfaceTree( "", new AetheriaRuntimeSurfaceComponent( "", "surface", new System.Collections.Generic.Dictionary(StringComparer.Ordinal), Array.Empty()), Array.Empty()); } var treeFields = reader.ReadArrayHeader(); var id = ReadFieldString(ref reader, treeFields, 0, ""); var root = ReadFieldSurfaceComponent(ref reader, treeFields, 1); var styles = ReadFieldSurfaceStyles(ref reader, treeFields, 2); for (var field = 3; field < treeFields; field++) { reader.Skip(); } return new AetheriaRuntimeSurfaceTree(id, root, styles); } private static AetheriaRuntimeSurfaceComponent ReadFieldSurfaceComponent( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return new AetheriaRuntimeSurfaceComponent( "", "", new System.Collections.Generic.Dictionary(StringComparer.Ordinal), Array.Empty()); } var componentFields = reader.ReadArrayHeader(); var id = ReadFieldString(ref reader, componentFields, 0, ""); var kind = ReadFieldString(ref reader, componentFields, 1, ""); var props = ReadFieldStringMap(ref reader, componentFields, 2); var children = ReadFieldSurfaceComponents(ref reader, componentFields, 3); var stateBindings = ReadFieldSurfaceStateBindings(ref reader, componentFields, 4); var embeddedDocuments = ReadFieldEmbeddedDocumentSlots(ref reader, componentFields, 5); var layout = ReadFieldStringMap(ref reader, componentFields, 6); var style = ReadFieldStringMap(ref reader, componentFields, 7); for (var field = 8; field < componentFields; field++) { reader.Skip(); } return new AetheriaRuntimeSurfaceComponent(id, kind, props, children, stateBindings, embeddedDocuments, layout, style); } private static System.Collections.Generic.IReadOnlyList ReadFieldEmbeddedDocumentSlots( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return Array.Empty(); } var count = reader.ReadArrayHeader(); var slots = new AetheriaRuntimeEmbeddedDocumentSlot[count]; for (var item = 0; item < count; item++) { var slotFields = reader.ReadArrayHeader(); var slotId = ReadFieldString(ref reader, slotFields, 0, ""); var documentId = ReadFieldString(ref reader, slotFields, 1, ""); var schemaId = ReadFieldString(ref reader, slotFields, 2, ""); var presentationKind = ReadFieldString(ref reader, slotFields, 3, ""); var routeKind = ReadFieldString(ref reader, slotFields, 4, nameof(CultMeshLocalityKind.Automatic)); var routeDescription = ReadFieldString(ref reader, slotFields, 5, ""); for (var field = 6; field < slotFields; field++) { reader.Skip(); } slots[item] = new AetheriaRuntimeEmbeddedDocumentSlot( slotId, documentId, schemaId, presentationKind, new CultMeshRouteHint( Enum.TryParse(routeKind, true, out var locality) ? locality : CultMeshLocalityKind.Automatic, routeDescription)); } return slots; } private static System.Collections.Generic.IReadOnlyList ReadFieldSurfaceStateBindings( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return Array.Empty(); } var count = reader.ReadArrayHeader(); var bindings = new CultMeshStateBindingDescriptor[count]; for (var item = 0; item < count; item++) { var bindingFields = reader.ReadArrayHeader(); var targetProp = ReadFieldString(ref reader, bindingFields, 0, "value"); var pointerId = ReadFieldString(ref reader, bindingFields, 1, ""); var sourceId = ReadFieldString(ref reader, bindingFields, 2, ""); var schemaId = ReadFieldString(ref reader, bindingFields, 3, ""); var routeKind = ReadFieldString(ref reader, bindingFields, 4, ""); var routeDescription = ReadFieldString(ref reader, bindingFields, 5, ""); for (var field = 6; field < bindingFields; field++) { reader.Skip(); } bindings[item] = CultMesh.StateBindingRecord( targetProp, pointerId, sourceId, schemaId, routeKind, routeDescription).ToBinding(); } return bindings; } private static System.Collections.Generic.IReadOnlyList ReadFieldSurfaceComponents( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return Array.Empty(); } var count = reader.ReadArrayHeader(); var children = new AetheriaRuntimeSurfaceComponent[count]; for (var child = 0; child < count; child++) { children[child] = ReadFieldSurfaceComponent(ref reader, 1, 0); } return children; } private static System.Collections.Generic.IReadOnlyDictionary ReadFieldStringMap( ref MessagePackReader reader, int fields, int index) { var values = new System.Collections.Generic.Dictionary(StringComparer.Ordinal); if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return values; } var count = reader.ReadMapHeader(); for (var item = 0; item < count; item++) { values[reader.ReadString() ?? ""] = reader.ReadString() ?? ""; } return values; } private static System.Collections.Generic.IReadOnlyList ReadFieldSurfaceStyles( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return Array.Empty(); } var count = reader.ReadArrayHeader(); var styles = new AetheriaRuntimeSurfaceStyleToken[count]; for (var style = 0; style < count; style++) { var styleFields = reader.ReadArrayHeader(); var name = ReadFieldString(ref reader, styleFields, 0, ""); var value = ReadFieldString(ref reader, styleFields, 1, ""); for (var field = 2; field < styleFields; field++) { reader.Skip(); } styles[style] = new AetheriaRuntimeSurfaceStyleToken(name, value); } return styles; } private static System.Collections.Generic.IReadOnlyList ReadFieldSurfaceCommands( ref MessagePackReader reader, int fields, int index) { if (index >= fields || reader.NextMessagePackType == MessagePackType.Nil) { if (index < fields) reader.ReadNil(); return Array.Empty(); } var count = reader.ReadArrayHeader(); var commands = new AetheriaRuntimeSurfaceCommandTemplate[count]; for (var command = 0; command < count; command++) { var commandFields = reader.ReadArrayHeader(); var name = ReadFieldString(ref reader, commandFields, 0, ""); var label = ReadFieldString(ref reader, commandFields, 1, ""); var schemaId = ReadFieldString(ref reader, commandFields, 2, ""); var routeKind = commandFields > 3 ? ReadFieldString(ref reader, commandFields, 3, "") : nameof(CultMeshLocalityKind.Automatic); var routeDescription = commandFields > 4 ? ReadFieldString(ref reader, commandFields, 4, "") : ""; for (var field = 5; field < commandFields; field++) { reader.Skip(); } commands[command] = new AetheriaRuntimeSurfaceCommandTemplate( CultMesh.OperationBindingRecord( name, label, schemaId, routeKind, routeDescription).ToBinding()); } return commands; } private static string ReadFieldString(ref MessagePackReader reader, int fields, int index, string fallback) { return index >= fields ? fallback : reader.ReadString() ?? fallback; } private static int ReadFieldInt32(ref MessagePackReader reader, int fields, int index, int fallback) { if (index >= fields) { return fallback; } if (reader.NextMessagePackType == MessagePackType.Nil) { reader.ReadNil(); return fallback; } return reader.ReadInt32(); } private static long ReadFieldInt64(ref MessagePackReader reader, int fields, int index, long fallback) { if (index >= fields) { return fallback; } if (reader.NextMessagePackType == MessagePackType.Nil) { reader.ReadNil(); return fallback; } return reader.ReadInt64(); } } }