View on GitHub

FOnline Engine

Flexible cross-platform isometric game engine

Engine Architecture

This document gives a source-grounded map of the FOnline engine layers. Use it when deciding where a behavior belongs before opening subsystem-specific docs.

Big picture

FOnline is organized around a reusable engine embedded by a game project. The game project owns content, scripts, product configuration, and release policy; the engine owns reusable runtime systems, tools, generated API infrastructure, platform frontends, and build composition.

The main layers are:

Application layer

Source/Applications/ is the practical entry-point directory. It contains app wrappers such as:

BuildTools/cmake/stages/Applications.cmake wires these files into project-specific targets based on build options such as client/server/tool/platform/library modes. Avoid hard-coding target names in engine docs: target names are often derived from the embedding project’s FO_DEV_NAME and presets.

See Applications.md for the application map.

Source paths inspected

Common runtime layer

Source/Common/ holds shared concepts used by client, server, tools, and scripts. Important entry points include:

This layer should stay reusable. Game rules should generally be expressed through content/scripts or project-native extensions, not by embedding one project’s policy into common engine code.

Runtime random state

random_generator owns its own state: capture_state() returns the four 64-bit words that define the sequence, and restore_state() puts them back, rejecting the all-zero state because xoshiro256++ sits at a fixed point there. BaseEngine::CaptureRandomState() and RestoreRandomState() delegate to the generator under the mutex it shares with random draws. There is no engine-level serialization format; a caller that needs to store the state serializes the four words itself.

This API is a persistence primitive, not a complete snapshot boundary. An authoritative server must still stop gameplay mutation before it captures the generator together with the corresponding world, time, event, and storage state. Client presentation, transport, and subsystem-specific generators are independent and are not included in this state.

The server-side RunInQuiescence() operation supplies that reusable in-process stop boundary: new connection admission closes, main/worker gameplay execution drains, frame/synchronized time and delayed-job scheduling freeze, the live entity graph is covered, and synchronized-time/RNG state is captured before a callback runs. ServerEngine::CreateSnapshot() composes the stable subset: it rejects counted runtime-only script/delayed/time-event/movement blockers, flushes exact time/id, and returns the database payload bytes together with the state that describes them. Fresh construction takes that pair back, restores the random state before startup jobs, loads the payload into storage, and validates its time/id before gameplay hooks. Atomic slot publication, persistent time-event/movement forms, project eligibility, UI policy, integrity/rotation, and coordinated client reload remain embedding-layer work. See ServerRuntime.md and Persistence.md for the exact guarantees and exclusions.

Client and server layers

Source/Client/Client.h includes the client-side composition points: application integration, resource/cache access, views for critters/items/locations/maps, effects, rendering-facing structures, and client connection code.

Source/Server/Server.h includes authoritative runtime pieces: entities, managers, database, geometry, scripting-facing server objects, client validation, networking, and updater backend support.

Treat client and server docs as separate because their ownership differs:

Frontend layer

Source/Frontend/Application.h and the related Application*.cpp / Rendering*.cpp files abstract platform app startup and rendering backends. This layer is where headless/stub/native frontend differences belong, not in game docs.

Platform workflow docs:

Scripting layer

Source/Common/ScriptSystem.* defines the common script-system abstraction. Source/Scripting/ provides runtime-specific method registration and integration folders:

The engine owns the reusable script/native bridge. A game project owns concrete game script modules and gameplay logic.

Build and generation layer

BuildTools/cmake/stages/ is the staged CMake pipeline. Current stage files include:

These stages compose engine code with embedding-project configuration. Read BuildWorkflow.md before changing build behavior.

Typical runtime flow

A normal embedding-project workflow looks like this:

  1. The game repository configures CMake from the project root.
  2. BuildTools loads project options and engine sources.
  3. Codegen and baking steps prepare generated API/resources/scripts.
  4. Applications are built from Source/Applications/ entry points.
  5. Runtime starts through the selected app: client, server, mapper, baker, test app, or platform package.
  6. Client/server/tools use common runtime services and call into game-owned scripts/content where appropriate.

Where to document changes