Client Runtime
Engine-owned documentation. This page describes reusable client runtime behavior in
Source/Client/; game UI policy, gameplay rules, and concrete content remain in the embedding project.
Purpose
The client runtime turns baked resources, server state, local input, and scripts into the player-facing game view. It does not own game design decisions. Instead, it provides the reusable engine pieces that a game project drives through scripts, configuration, resources, and server messages.
Read this page together with:
- EntityModel.md for the entity/property/prototype model that client views wrap.
- MapsMovementGeometry.md for map positions, path finding, line tracing, and movement contexts.
- Networking.md for command buffers, transports, and property sync.
- FrontendAndRendering.md for platform windows, input, audio, and renderer backends.
- WebDebugging.md, AndroidDebugging.md, and Debugging.md for platform-specific validation flows.
Source paths inspected
Source/Client/Client.hSource/Client/Client.cppSource/Client/ClientConnection.hSource/Client/ClientConnection.cppSource/Client/ResourceManager.hSource/Client/ResourceManager.cppSource/Client/FontManager.hSource/Client/FontManager.cppSource/Client/MapView.hSource/Client/MapView.cppSource/Scripting/ClientMapScriptMethods.cppSource/Client/CritterView.hSource/Client/CritterHexView.hSource/Client/ItemView.hSource/Client/ItemHexView.hSource/Client/LocationView.hSource/Client/PlayerView.hSource/Client/DefaultSprites.hSource/Client/DefaultSprites.cppSource/Client/3dStuff.hSource/Client/3dStuff.cppSource/Client/3dAnimation.hSource/Client/3dAnimation.cppSource/Client/ModelSprites.hSource/Client/ModelSprites.cppSource/Client/ParticleSprites.hSource/Client/ParticleSprites.cppSource/Client/RenderTarget.hSource/Client/RenderTarget.cppSource/Tests/Test_ClientEngine.cppSource/Tests/Test_ClientRuntimeApi.cppSource/Tests/Test_ClientDataValidation.cppSource/Tests/Test_ClientServerIntegration.cpp
Runtime owner: ClientEngine
ClientEngine in Source/Client/Client.h is the central client-side engine object. It derives from BaseEngine and AnimationResolver, owns the high-level client lifecycle, and exposes event hooks used by client scripts.
Major responsibilities:
- initialize and shut down the client runtime;
- run the per-frame
MainLoop(); - process application input events;
- connect and disconnect from a server through
ClientConnection; - create, register, unregister, and look up client-side entities by id;
- receive and apply network messages for critters, items, maps, custom entities, time sync, movement, actions, and properties;
- own client-facing managers such as sprites, effects, fonts, sounds, video playback, resources, cache storage, and render targets;
- raise engine events such as
OnStart,OnLoop,OnConnected,OnDisconnected, render-map stages, input events, entity in/out events, and map load/unload events.
ClientEngine is intentionally broad: it is the composition root where Common-layer data (Entity, properties, prototypes, networking buffers) meets Frontend-layer services (Application, render, input, audio) and game scripts.
Client lifecycle
A typical client lifetime has these phases:
- Application initialization happens in the frontend layer.
Applicationowns the main window, renderer, input, and audio. See FrontendAndRendering.md. - Resource filesystem selection starts through
GetClientResources(GlobalSettings&)inSource/Client/Client.cpp, which builds the client-sideFileSystemview used by runtime managers. - Engine construction wires settings, resources, the main application window, generated metadata, script modules, and client managers.
OnStart/script initialization gives scripts their first client-side entry point.Source/Tests/Test_ClientEngine.cppvalidates that script module init and loop calls are callable on a self-contained client runtime.- The main loop processes frontend input, network packets, scripted loop callbacks, visual effects, video playback, map processing, and rendering-facing updates.
- Network connection starts with
Connect(), which delegates transport setup and handshake work toClientConnection. - Map and entity state arrive through network messages, are represented as client view entities, and are updated through property sync and movement/action packets.
- Shutdown disconnects networking, destroys inner entities, clears caches and render targets, and releases frontend resources.
When changing startup or shutdown behavior, keep script events, manager lifetime, entity registration, and network callbacks in sync; these paths are tightly coupled.
Server connection and message dispatch
ClientConnection (Source/Client/ClientConnection.h, Source/Client/ClientConnection.cpp) owns the client-side transport state. It hides whether the current connection is interthread, TCP sockets, or UDP-capable sockets.
Important responsibilities:
Connect()selects the connection mode fromClientNetworkSettingsand creates aNetworkClientConnection;CreateNetworkConnection(bool use_udp)selects socket or UDP socket transport;Process()advances connect, receive, dispatch, and send work;Disconnect()tears down active transport and raises the configured disconnect handler;TryFallbackToTcp()provides a fallback path when UDP setup fails;Net_SendHandshake(),Net_OnHandshakeAnswer(), andNet_OnPing()handle connection-level protocol messages;AddMessageHandler(NetMessage, MessageCallback)binds protocol messages toClientEngine::Net_On...handlers.
ClientEngine owns the semantic handlers. Examples include Net_OnInitData, Net_OnAddCritter, Net_OnRemoveCritter, Net_OnProperty, Net_OnLoadMap, Net_OnSomeItems, Net_OnRemoteCall, Net_OnAddCustomEntity, and Net_OnRemoveCustomEntity.
For protocol format details, use Networking.md. For client/server handshake validation, see Source/Tests/Test_ClientServerIntegration.cpp, especially ClientAndServerHandshakeOverInterthreadTransport.
Client-side script continuations scheduled through ScheduleDelayedCallback() are processed once per main-loop pass from a snapshot of callbacks already due at the start of that pass. A callback that schedules another zero-delay callback, including Yield(0), resumes on the next pass instead of re-entering immediately. This prevents script wait loops from starving the next network/input tick.
Entity and view model
Client-side game objects are not raw server entities. They are view entities that combine Common-layer entity data with client-only rendering, input, and presentation state.
Primary view types:
PlayerView— client representation of the current player entity.LocationView— client-side location entity.MapView— map entity plus map rendering, local spatial indexes, fog, lighting, scrolling, zoom, and hit testing.CritterView— critter entity visible outside the current map context.CritterHexView— critter placed on a map hex, with movement, direction, and map rendering behavior.ItemView— item entity visible in inventory or generic client contexts.ItemHexView— item placed on a map hex, with map flags, sprite placement, blockers, and lighting implications.- custom client entities — created by
CreateCustomEntityView()when a synced custom entity entry arrives.
ClientEngine::RegisterEntity() and ClientEngine::UnregisterEntity() maintain the id-to-entity lookup used by network handlers and scripts. Source/Tests/Test_ClientEngine.cpp validates that client entities can be registered and removed from the lookup.
Critter model animation
3D critter models use separate body/action and movement animation controllers. ModelInstance::PlayAnim() applies
animation-specific speed (AnimSpeed) to the body/action controller, while RefreshMoveAnimation() assigns gait and
movement-speed scaling to the movement controller’s track. When both are active, the movement controller advances with
the model base/link/global speed only; it must not inherit the current body action’s AnimSpeed, otherwise fast actions
such as use/pick-up make the leg cycle run too quickly while the critter is moving.
MapView: map presentation and local spatial state
MapView is the largest client view class because it bridges several subsystems:
- map file/static-data loading through
LoadFromFile()andLoadStaticData(); - map processing through
Process(); - map rendering through
DrawMap()and staged render events onClientEngine; - field indexes for items and critters;
- coordinate conversion, zoom, scrolling, screen-to-map and map-to-screen transformations;
- path finding and cut-path helpers built on
PathFinding::FindPath(); - line tracing for bullets and light fans;
MapView::ApplyLightFantraces every light source out to its fullDistancein hexes (kept at >= 1) and forwards per-light falloff metadata to primitive shaders viaPrimitivePoint::EggData→ vertex attribute slot 3 (InTexEggCoord):LightFanToPrimitveswrites the traced radius (hexes) and the normalized center alpha so a shader can reconstruct each fragment’s hex-distance-from-edge and fade over a fixed hex band independent of the light’s total length. A critter’s light fan follows the critter’s sprite offset (HexView::GetSpriteOffsetPtr()) so the light stays exactly under the sprite — the two must never diverge. The offset is kept bounded at the source:MovingContext::EvaluateRawProgress’scurrent_hexcan lag the smooth lerp, and client prediction reconciliation inReceiveCritterMovingcan fold an inter-hex delta into the offset during rapid step taps, butCritterHexView::StopMovingcashes any accumulated offset back into the hex (snapping to the hex the sprite actually reached and keeping only the sub-hex remainder, withhex + offsetinvariant so the sprite never jumps), so the offset cannot run away and drag the fan off the critter. Items use the sameGetSpriteOffsetPtr(); - fog-of-war layers;
- local lighting sources and render targets;
- mapper mode helpers used by engine tools.
MapView is still a client-side view over the Common map model. Reusable coordinate/pathfinding rules belong in MapsMovementGeometry.md; presentation details such as render targets, light textures, transparent eggs, map scrolling, and hit testing belong here and in FrontendAndRendering.md.
The reusable map presentation API includes SetExtraScrollOffset() for script-owned transient camera offsets. The engine applies the offset to the map view, but game-specific screen effects such as quake/shake timing and fade overlays are owned by embedding-project scripts.
Resources, sprites, effects, and render targets
The client resource path starts with a FileSystem from GetClientResources() and is organized by runtime managers:
ResourceManagerindexes resource files, resolves item default sprites, loads and caches critter animation frames, handles Fallout-style animation frame mapping, and exposes sound-name mappings.SpriteManagerowns sprite factories, atlases, primitive drawing, draw ordering, scissor stack, window/screen sizing, and render-target drawing.DefaultSpriteFactoryloads atlas sprites and sprite sheets from default image/animation resources.ModelSpriteFactoryturns model resources into atlas-backed sprites by rendering model frames into a render target.ParticleSpriteFactorydoes the same for particle resources.EffectManagerloads default/minimal effects, resolves script-selected effects, and updates per-frame effect buffers.FontManagerloads fonts and formats/draws text, including inline color tags.RenderTargetManagercreates, resizes, pushes, pops, reads, clears, dumps, and deletes offscreen render targets.
For 3D critter views, idle refresh plays alive-state animations from the beginning. Dead condition idles freeze on their final frame. Other non-alive condition idles freeze on their first frame, so embedding projects should author that first frame as the intended resting pose for the condition.
These managers are renderer-facing but not renderer-specific. They talk through IAppRender / Renderer abstractions, so the same client logic can run against OpenGL, Direct3D, or the null renderer depending on platform/build configuration.
Fonts and Inline Color Tags
FontManager::FormatText() strips @color:0xBBGGRR@ / @color:0xAABBGGRR@ tags and records the parsed ucolor value in the formatted text’s per-glyph color buffer during draw formatting. The reset tag is @color@; it restores the previous inline color, or the base draw color when no inline color is active. FontFlag::NoColorize still strips these tags, but keeps rendering with the caller-provided base color.
Input and script-facing hooks
ClientEngine::ProcessInputEvent() receives frontend InputEvent values and raises higher-level script events such as:
OnMouseDown,OnMouseUp,OnMouseMove;OnTouchTap,OnTouchDoubleTap,OnTouchScroll,OnTouchZoom;OnKeyDown,OnKeyUp,OnInputLost;OnScreenScrollandOnScreenSizeChanged;- map render-stage events such as
OnRenderMap_BeforeTiles,OnRenderMap_AfterSprites, andOnRenderMap_AfterFlushMap.
Input semantics originate in Source/Frontend/Application.h; game-specific UI behavior should stay in scripts and GUI resources owned by the embedding project.
Client scripts can synthesize local input through the same runtime path for automation and embedded-client probes. Game.SimulateMouseClick(pos, button) sends mouse move/click or wheel events, Game.SimulateKeyPress(key, text) sends one key down/up pair, and Game.SimulateKeyboardPress(key1, key2, key1Text, key2Text) remains available for two-key sequences.
For local critter movement prediction, ClientEngine::CritterMoveTo() synchronizes any active MovingContext to the current client frame before starting a new movement or sending a stop request. It then normalizes the local hex/offset pair before the next request is sent, so rapid start/stop input does not report one-frame-stale or overlarge offsets to the server.
Client-side validation tests
Use the smallest relevant test scope when changing client runtime behavior:
Source/Tests/Test_ClientEngine.cpp— self-contained client engine startup, entity registration, script module initialization, and loop callbacks.Source/Tests/Test_ClientServerIntegration.cpp— client/server handshake over interthread transport and observable client connection events.Source/Tests/Test_ClientDataValidation.cpp— inbound remote-call payload validation for UTF-8, enums, floats, hashed strings, bools, truncation, and ref-type payloads.Source/Tests/Test_ClientRuntimeApi.cpp— client runtime ABI/export/result validation for the host/runtime split described in ClientUpdater.md.
Exact test target names are generated by the embedding project’s CMake/BuildTools configuration; do not hard-code one project’s target names in engine docs.
Change checklist
When changing client runtime code, verify:
ClientEnginestartup/shutdown still leaves no stale registered entities or render targets.- New network messages are documented in Networking.md and bound through
ClientConnection::AddMessageHandler(). - New synced state has a clear ownership boundary between Common entities/properties and client-only view state.
- Map presentation changes do not duplicate coordinate/pathfinding rules already owned by MapsMovementGeometry.md.
- Resource changes describe whether they affect
ResourceManager,SpriteManager, a sprite factory,EffectManager, orRenderTargetManager. - Platform-specific rendering/input implications are reflected in FrontendAndRendering.md, WebDebugging.md, or AndroidDebugging.md as appropriate.