Game state loader¶
This module drives the game-loading and update loop: it talks to com.stratkit.server-communication to post requests, hands the response to com.stratkit.client-api's walker to deserialize, and applies the result to the world. It is also where every other module registers the handlers that read the deserialized state.
Configuration¶
This module is configured through a GameConfig object:
- ServerAddress: The game server URL (scheme, host, port).
- UserAuth: User authentication token or password for alpha servers.
- UserId: The user account id.
- GameId: The game id.
- GameStateUpdateInterval: The game state polling interval in seconds (optional, default is 60 seconds).
Registering a handler¶
ClientApiPassSetupSystem creates the ClientApiRegistry singleton (ClientApiRegistryContainer) before any other module's setup system runs, and builds the ClientApiWalker from it only once every module has had the chance to register — after AllModulesInitialisedTag is set. A module's own setup system fetches the registry and registers its handlers there, typically disabling itself once done:
[UpdateInGroup(typeof(InitializationSystemGroup))]
internal sealed partial class AIStateLoaderSetupSystem : SystemBase {
protected override void OnCreate() => RequireForUpdate<ClientApiRegistryContainer>();
protected override void OnUpdate() {
ClientApiRegistry registry = SystemAPI.ManagedAPI.GetSingleton<ClientApiRegistryContainer>().Value;
registry.AddAIStateHandler(ClientApiPaths.AIState.Self, new AIStateDataHandler());
Enabled = false;
}
}
A handler registered after the walker is built is never dispatched to, so registration must happen before that point — see com.stratkit.client-api for how a handler itself is implemented.
Talking to the game server¶
GameServerClient, held on ClientApiPassContainer, speaks the Client API: it builds requests, writes them into its native buffer, and posts them through com.stratkit.server-communication's GameServerTransport. It sits next to the walker because a state response's envelope can only be read while the response is walked.
- Loading a game.
LoadGameAsync()activates the game and requests the first state in one round trip, so it hands back both the activation result and an openGameStateResponse. Call it once at the start of a game session. It retries the whole sequenceGameServerClientSettings.LoadGameAttemptstimes. - Updating a game.
RequestGameStateAsync(versions, actions)polls for changes since the givenStateVersions, with user actions along for the ride.RequestGameStateSingleAsyncasks for one state.Stratkit.ClientApi.SupTypeVersioning.UsesTimeStamp(SupType)says which states also send their timestamp. - Buffer sizes.
GameStateLoaderModule'sClientApiCodecSettingssizes the request writer, the state reader and the per-activation reader. All three are created inClientApiPassSetupSystemor by the client it builds. - Server switches. A
ClientSwitchServerExceptionpoints the client at the named host and repeats the request unchanged, up to three responses per request. That applies to the activation and to state requests alike.
Running a pass¶
GameStatePass.Start starts one deserialization pass — request, read, dispatch — against the ClientApiPassContainer singleton (the client, reader, context and walker that live for the world's lifetime) and moves the game state entity to GameUpdateState.ApplyingUpdate. ApplyGameStateSystem picks up the resulting GameStatePassResult once the pass completes and plays its command buffer into the world.
Every request path — the initial game load, the periodic poll, queued user actions and a single-state fetch — funnels through this one pass.
Issue user actions¶
When the user interacts with the game in a way that affects the state, the server needs to process and verify this "user action", sending an updated game state in response. User actions are typed as Stratkit.ClientApi.Actions.IClientUserAction, generated from the Client-API schema — one DTO per action, constructed with an object initializer. To issue one, create a new entity with a ServerCommandRequest component wrapping it; RequestServerCommandSystem picks it up, sends it along with the next poll, and destroys the entity.