Quick Start
This guide walks through connecting to a ConvergeDB server, defining an entity kind, writing data, and subscribing to changes.
Prerequisites
Section titled “Prerequisites”- .NET 8.0 or later
- A running ConvergeDB server (default port: 3727)
Install the SDK
Section titled “Install the SDK”dotnet add package Convergence.ClientThe Convergence.Client package includes a source generator that produces serialization code for your entity types.
Define an entity kind
Section titled “Define an entity kind”Entity kinds are defined as C# structs with attributes. The source generator handles serialization.
using Convergence.Client;
[ConvergenceEntity("Player")]public partial struct Player{ [Field(0)] public ulong Id { get; set; } [Field(1)] public uint Health { get; set; } [Field(2)] public float PosX { get; set; } [Field(3)] public float PosY { get; set; } [Field(4, MaxLength = 32)] public string Name { get; set; }
public ReadOnlyMemory<byte> EntityId { get; init; }}Key points:
[ConvergenceEntity("Player")]names the kind on the server.[Field(N)]assigns a stable ordinal to each field. These ordinals determine the wire layout and must never change.EntityIdis a required 32-byte property that uniquely identifies each entity within the kind.
Connect and register
Section titled “Connect and register”using Convergence.Client;
// Connect to the serverawait using var client = await ConvergenceClient.ConnectAsync(new ConvergenceOptions{ Host = "127.0.0.1", Port = 3727, SourceId = 1, // unique writer identity, 0-63});
// Register the entity kind (safe to call on every startup)KindHandle<Player> players = await client.RegisterKindAsync<Player>();RegisterKindAsync uses register-if-not-exists semantics. If the kind already exists with the same schema, it returns the existing KindId. If new fields have been appended, the schema is automatically migrated.
Write data
Section titled “Write data”var player = new Player{ EntityId = EntityId.FromGuid(Guid.NewGuid()), Id = 42, Health = 100, PosX = 1.5f, PosY = -2.3f, Name = "Alice",};
// Assert the entity's current stateawait players.AssertAsync(player);AssertAsync serializes the entity, buffers it, and flushes to the server. The server creates the entity if it does not exist, or updates it if it does.
Read data
Section titled “Read data”Player? result = await players.QueryAsync(player.EntityId);if (result is { } p){ Console.WriteLine($"Found: {p.Name} at ({p.PosX}, {p.PosY})");}Point reads are served entirely from the server’s in-memory entity table with no disk I/O.
Subscribe to changes
Section titled “Subscribe to changes”await foreach (var change in players.SubscribeAsync(bootstrap: true)){ Console.WriteLine($"{change.Type}: {change.Entity?.Name} (v{change.Version})");}With bootstrap: true, the server first sends a snapshot of all existing entities, then transitions to live change notifications. This ensures the subscriber starts from a complete view with no gaps.
See Subscriptions and Bootstrap for the full semantics.
What’s next?
Section titled “What’s next?”- Core Concepts for the full mental model
- Schema Design for field types, arrays, and structs
- Writer’s Guide for write semantics, batching, and epochs
- Reader’s Guide for subscriptions, bootstrap, and reconnection
- Limits for hard constraints to keep in mind