Add schema discovery messages to CultNet
This commit is contained in:
parent
73a30052c7
commit
0e452b096c
3 changed files with 136 additions and 0 deletions
|
|
@ -15,6 +15,8 @@ namespace GameCult.Networking
|
|||
// Sample application payload tags preserved for cross-runtime compatibility.
|
||||
Union(5, typeof(ChangeNameMessage)),
|
||||
Union(6, typeof(ChatMessage)),
|
||||
Union(7, typeof(SchemaCatalogRequestMessage)),
|
||||
Union(8, typeof(SchemaCatalogResponseMessage)),
|
||||
MessagePackObject]
|
||||
public abstract class Message
|
||||
{
|
||||
|
|
@ -110,4 +112,95 @@ namespace GameCult.Networking
|
|||
[Key(0)] public string Error = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests a catalog of schemas that the remote runtime can safely exchange.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public class SchemaCatalogRequestMessage : Message
|
||||
{
|
||||
/// <summary>
|
||||
/// Correlation identifier for the discovery request.
|
||||
/// </summary>
|
||||
[Key(0)] public string MessageId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the remote runtime should include inline JSON schema bodies.
|
||||
/// </summary>
|
||||
[Key(1)] public bool IncludeSchemaJson;
|
||||
|
||||
/// <summary>
|
||||
/// Optional schema-id filter.
|
||||
/// </summary>
|
||||
[Key(2)] public string[]? SchemaIds;
|
||||
|
||||
/// <summary>
|
||||
/// Optional schema-kind filter.
|
||||
/// </summary>
|
||||
[Key(3)] public string[]? Kinds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes one schema that the runtime knows how to exchange safely.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public class SchemaDescriptorMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Stable canonical schema identifier.
|
||||
/// </summary>
|
||||
[Key(0)] public string SchemaId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Discovery kind such as wire_message, document_payload, or shared_contract.
|
||||
/// </summary>
|
||||
[Key(1)] public string Kind = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional runtime message schema version.
|
||||
/// </summary>
|
||||
[Key(2)] public string? SchemaVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Optional CultCache/CultNet document type name.
|
||||
/// </summary>
|
||||
[Key(3)] public string? DocumentType;
|
||||
|
||||
/// <summary>
|
||||
/// Human-facing schema title.
|
||||
/// </summary>
|
||||
[Key(4)] public string? Title;
|
||||
|
||||
/// <summary>
|
||||
/// Wire contracts that can transport this schema safely.
|
||||
/// </summary>
|
||||
[Key(5)] public string[] WireContracts = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 hash of the canonical schema JSON.
|
||||
/// </summary>
|
||||
[Key(6)] public string ContentHash = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional canonical JSON Schema body as a string.
|
||||
/// </summary>
|
||||
[Key(7)] public string? SchemaJson;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remote runtime's schema catalog for safe exchange planning.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public class SchemaCatalogResponseMessage : Message
|
||||
{
|
||||
/// <summary>
|
||||
/// Correlation identifier matching the request.
|
||||
/// </summary>
|
||||
[Key(0)] public string MessageId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Discovered schemas available for safe exchange.
|
||||
/// </summary>
|
||||
[Key(1)] public SchemaDescriptorMessage[] Schemas = Array.Empty<SchemaDescriptorMessage>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ Keep the distinction clean:
|
|||
- auth/session semantics belong to the core library
|
||||
- application payload contracts should be explicit, versioned, and kept in sync
|
||||
across runtimes
|
||||
- runtimes should be able to ask each other which schemas they speak before
|
||||
pretending a shared pipe implies shared understanding
|
||||
- if multiple apps share a message contract, they should be able to talk
|
||||
directly without bespoke translation sludge
|
||||
|
||||
|
|
@ -41,6 +43,8 @@ Sample payloads:
|
|||
|
||||
- `ChangeNameMessage`
|
||||
- `ChatMessage`
|
||||
- `SchemaCatalogRequestMessage`
|
||||
- `SchemaCatalogResponseMessage`
|
||||
|
||||
Those now live under `Samples/` to make it obvious they are example application
|
||||
messages, not the entire meaning of the library.
|
||||
|
|
|
|||
|
|
@ -107,6 +107,45 @@ namespace GameCult.Networking.Tests
|
|||
Assert.That(roundTrip.Nonce, Is.EqualTo(message.Nonce));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MessageSerialization_RoundTrips_SchemaCatalogMessages()
|
||||
{
|
||||
var client = new Client(DevelopmentClientSecurity);
|
||||
var message = new SchemaCatalogResponseMessage
|
||||
{
|
||||
MessageId = "catalog-1",
|
||||
Schemas =
|
||||
[
|
||||
new SchemaDescriptorMessage
|
||||
{
|
||||
SchemaId = "https://example.test/contracts/example.schema.json",
|
||||
Kind = "shared_contract",
|
||||
SchemaVersion = "example.contract.v0",
|
||||
Title = "Example Contract",
|
||||
WireContracts = ["cultnet.schema.v0", "gamecult.networking.v0"],
|
||||
ContentHash = "deadbeef",
|
||||
SchemaJson = "{\"type\":\"object\"}"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var serializationType = client.GetType().Assembly
|
||||
.GetType("GameCult.Networking.MessageSerialization", throwOnError: true)!;
|
||||
var serialize = serializationType.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Static)!
|
||||
.MakeGenericMethod(typeof(Message));
|
||||
var deserialize = serializationType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static)!
|
||||
.MakeGenericMethod(typeof(Message));
|
||||
|
||||
var payload = (byte[])serialize.Invoke(null, [message])!;
|
||||
var roundTrip = (SchemaCatalogResponseMessage)deserialize.Invoke(null, [payload])!;
|
||||
|
||||
Assert.That(roundTrip.MessageId, Is.EqualTo("catalog-1"));
|
||||
Assert.That(roundTrip.Schemas, Has.Length.EqualTo(1));
|
||||
Assert.That(roundTrip.Schemas[0].SchemaId, Is.EqualTo("https://example.test/contracts/example.schema.json"));
|
||||
Assert.That(roundTrip.Schemas[0].WireContracts, Is.EqualTo(["cultnet.schema.v0", "gamecult.networking.v0"]));
|
||||
Assert.That(roundTrip.Schemas[0].SchemaJson, Is.EqualTo("{\"type\":\"object\"}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MessageSerialization_Rejects_InvalidPayload()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue