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:
- Applications — executable and library entry points in
Source/Applications/. - Essentials — low-level platform, memory, filesystem, logging, serialization, sockets, and utilities in
Source/Essentials/. - Common runtime — shared engine model in
Source/Common/: entities, properties, prototypes, maps, networking primitives, config, scripts, and engine base services. - Client runtime — presentation/resource/network-client side in
Source/Client/. - Server runtime — authoritative world, managers, database backends, network-server side, and updater backend in
Source/Server/. - Frontend — application/window/rendering abstraction in
Source/Frontend/. - Scripting — AngelScript, Native, Managed, and script method registration in
Source/Scripting/. - Tools — baker, Mapper-centered editing, asset processors, and related developer tooling in
Source/Tools/. - BuildTools — CMake stages, helpers, toolchains, platform project generation, package layout, and validation support in
BuildTools/.
Application layer
Source/Applications/ is the practical entry-point directory. It contains app wrappers such as:
ClientApp.cppandClientLib.cppfor client host/runtime flows.ServerApp.cpp,ServerDaemonApp.cpp,ServerHeadlessApp.cpp, andServerServiceApp.cppfor server variants.MapperApp.cppfor the central interactive editing tool.BakerApp.cpp,ASCompilerApp.cpp, andManagedScriptBakerApp.cppfor generation/build support.TestingApp.cppfor test execution.
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
Source/Applications/Source/Common/EngineBase.hSource/Common/EngineBase.cppSource/Common/Entity.hSource/Common/Entity.cppSource/Common/ScriptSystem.hSource/Common/ScriptSystem.cppSource/Client/Client.hSource/Server/Server.hSource/Frontend/Application.hSource/Frontend/ApplicationInit.cppBuildTools/cmake/stages/Applications.cmake
Common runtime layer
Source/Common/ holds shared concepts used by client, server, tools, and scripts. Important entry points include:
EngineBase.h/EngineBase.cpp— base engine services and shared runtime state.Entity.h/Entity.cpp— exported entity concepts shared across runtime sides.Properties.h,EntityProperties.h,EntityProtos.h,ProtoManager.h— property/prototype model.ScriptSystem.h/ScriptSystem.cpp— script engine abstraction used by runtime sides and tools.Geometry.h,Movement.h,PathFinding.h,MapLoader.h— reusable map and movement primitives.NetBuffer.h,NetworkUdp.h— common networking primitives.ConfigFile.h,DataSource.h,FileSystem.h,CacheStorage.h— config and data access support.ImageWriter.h— TGA/PNG encoders for the diagnostic images the engine writes itself (screenshots, render-target and atlas dumps).
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:
- The client presents local views, resources, UI-facing objects, and network-client behavior.
- The server owns authoritative world state, persistence, entity managers, validation, and network-server behavior.
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:
Source/Scripting/AngelScript/Source/Scripting/Native/Source/Scripting/Managed/Source/Scripting/*ScriptMethods.cpp
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:
Init.cmakeProjectOptions.cmakeCoreLibs.cmakeThirdParty.cmakeEngineSources.cmakeCodegen.cmakeApplications.cmakeScriptsAndBaking.cmakePackages.cmakeFinalize.cmake
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:
- The game repository configures CMake from the project root.
- BuildTools loads project options and engine sources.
- Codegen and baking steps prepare generated API/resources/scripts.
- Applications are built from
Source/Applications/entry points. - Runtime starts through the selected app: client, server, mapper, baker, test app, or platform package.
- Client/server/tools use common runtime services and call into game-owned scripts/content where appropriate.
Where to document changes
- Architecture-wide behavior: this file.
- Source navigation: SourceTree.md.
- App entry points: Applications.md.
- Build workflow: BuildWorkflow.md and BuildToolsPipeline.md.
- Script/native boundary: Nullability.md and Scripting.md.
- Platform debugging: WebDebugging.md, AndroidDebugging.md, Debugging.md.