View on GitHub

FOnline Engine

Flexible cross-platform isometric game engine

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:

Source paths inspected

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:

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:

  1. Application initialization happens in the frontend layer. Application owns the main window, renderer, input, and audio. See FrontendAndRendering.md.
  2. Resource filesystem selection starts through GetClientResources(GlobalSettings&) in Source/Client/Client.cpp, which builds the client-side FileSystem view used by runtime managers.
  3. Engine construction wires settings, resources, the main application window, generated metadata, script modules, and client managers.
  4. OnStart/script initialization gives scripts their first client-side entry point. Source/Tests/Test_ClientEngine.cpp validates that script module init and loop calls are callable on a self-contained client runtime.
  5. The main loop processes frontend input, network packets, scripted loop callbacks, visual effects, video playback, map processing, and rendering-facing updates.
  6. Network connection starts with Connect(), which delegates transport setup and handshake work to ClientConnection.
  7. Map and entity state arrive through network messages, are represented as client view entities, and are updated through property sync and movement/action packets.
  8. 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:

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:

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:

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:

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:

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:

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: