View on GitHub

FOnline Engine

Flexible cross-platform isometric game engine

Scripting

Engine-owned documentation. This page describes reusable scripting runtime behavior in Source/Common/ScriptSystem.* and Source/Scripting/; concrete game scripts, quests, rules, and content policy belong to the embedding project.

Purpose

The scripting layer is the contract between the C++ engine runtime and game-authored behavior. It exposes engine entities, global services, events, remote calls, value types, collections, reflection helpers, and tool/frontend helpers to script code while keeping C++ ownership, metadata, nullability, persistence, networking, and validation in the engine.

Read this page together with:

Source paths inspected

Layer map

The scripting subsystem has four layers:

  1. Common runtime facadeSource/Common/ScriptSystem.h / .cpp define the backend-agnostic ScriptSystem, ScriptFuncDesc, ScriptFunc, FuncCallData, DataAccessor, native call adapters, init functions, loop callbacks, and type maps.
  2. Backend implementationSource/Scripting/AngelScript/ provides the current production backend. Mono and native scripting have placeholder/source roots, but AngelScript owns the implemented script compiler/runtime path in this tree.
  3. Script-visible native methodsSource/Scripting/*ScriptMethods.cpp files contain ///@ ExportMethod functions grouped by runtime side and receiver type. Codegen reads these annotations and emits method descriptors/wrappers.
  4. Core script library and game scriptsSource/Scripting/AngelScript/CoreScripts/*.fos provides engine-owned reusable script-side helpers. Embedding projects add their own .fos files and metadata through project configuration and resource/script baking.

The engine owns the reusable bridge. The embedding project owns game scripts and chooses which features are enabled through project configuration, build presets, and .fomain inputs.

ScriptSystem: backend-neutral dispatch

ScriptSystem is the C++ runtime facade used by client, server, mapper, tests, and script-aware tools. Its main jobs are:

ScriptFunc<TRet, Args...> normalizes native arguments into FuncCallData and catches script exceptions so callers can continue after a failed script callback. NativeDataProvider and NativeDataCaller adapt C++ arrays, dictionaries, entities, callbacks, value types, and mutable references to the generic call representation.

This boundary is also where generated nullability checks are inserted. NativeDataProvider::CheckArgNotNull() and CheckReturnNotNull() are called by codegen-generated MethodDesc::Call lambdas, not only by the AngelScript adapter. See Nullability.md for the full contract.

AngelScript runtime path

InitAngelScriptScripting() in Source/Scripting/AngelScript/AngelScriptScripting.cpp prepares the AngelScript runtime, creates an AngelScriptBackend, registers it at ScriptSystemBackend::ANGELSCRIPT_BACKEND_INDEX, and loads binary scripts from resources.

CompileAngelScript() is the compiler-side entry point used by tools/tests. It creates a standalone ScriptSystem, registers metadata, compiles text script files, and returns bytecode.

AngelScriptBackend owns the concrete engine instance and module lifecycle:

AngelScript is therefore used in two modes: compile-time tooling mode and runtime mode. The same metadata and type registration code must remain compatible with both.

Attributes, declarations, and metadata

Source/Scripting/AngelScript/AngelScriptAttributes.cpp parses engine-specific script attributes and declaration tags. Important contracts include:

These attributes are source-level contracts. AngelScript sees normalized declarations after preprocessing, while engine metadata and analyzers retain the higher-level FOnline-specific meaning.

Entities and properties in scripts

Source/Scripting/AngelScript/AngelScriptEntity.cpp registers script object types for engine entities, singleton-like components, property accessors, entity event types, and method dispatch. It bridges generated metadata with AngelScript registration calls so script code can work with engine entities through script-visible names such as critters, items, maps, locations, players, prototypes, abstracts, statics, holders, and property-backed components.

Entity lifetime is still owned by the engine runtime:

Use EntityModel.md for entity/property/prototype ownership and Persistence.md for database boundaries.

Remote calls and event callbacks

Source/Scripting/AngelScript/AngelScriptRemoteCalls.cpp registers remote caller object types such as RemoteCaller and CritterRemoteCaller. Remote-call declarations are metadata-backed, and runtime handling is split by side:

Events and remote calls are intentionally separate concepts. Events describe engine/runtime lifecycle and gameplay notifications; remote calls describe network-addressable script entry points. Both rely on metadata signatures, nullability contracts, and generated descriptors.

Native script method exports

Native script APIs are grouped by file name:

Each exported function is marked with ///@ ExportMethod and normally starts with a side/type prefix such as Server_Map_, Client_Game_, Common_ImGui_, or Mapper_Game_. Codegen turns these declarations into script-visible method descriptors and backend call wrappers. See ScriptMethodsMap.md for the per-file map and counts.

When adding a method, route it to the side that owns the state it mutates. For example, authoritative item creation belongs under server methods, while sprite/UI helpers belong under client/common frontend methods.

Core scripts

The engine-owned AngelScript core library lives in Source/Scripting/AngelScript/CoreScripts/ and includes reusable modules such as:

Treat these files as engine library code. Game-specific script modules should live in the embedding project instead of expanding the engine core script library with project policy.

Build and baking flow

BuildTools/cmake/stages/ScriptsAndBaking.cmake wires script compilation into the project build:

Script compilation and resource baking are adjacent but not identical. Script compilation produces bytecode/runtime inputs; baking packages resources and metadata for runtime consumption. See BakingPipeline.md for resource baking.

Mono and native scripting roots

Source/Scripting/Mono/ contains C# support files such as AssemblyInfo.cs, BasicTypes.cs, Entity.cs, Initializator.cs, MapSprite.cs, and Link.xml. BuildTools can wire Mono compilation when FO_MONO_SCRIPTING is enabled.

Source/Scripting/Native/ currently contains .keepalive, marking the source-root location for native scripting integration. Do not document Native or Mono as equivalent to the AngelScript runtime unless the implementation and tests are expanded.

Tests to inspect

Script behavior is covered by focused tests:

Use these tests as executable documentation when changing script registration, generated wrappers, method signatures, nullability, event declarations, or remote-call dispatch.

Change routing

Validation checklist

  1. If signatures or annotations changed, regenerate code and inspect generated metadata/wrapper diffs.
  2. Compile AngelScript through the embedding project’s CompileAngelScript target or equivalent AS compiler app.
  3. Run the smallest affected script tests, starting with Test_AngelScriptAttributes, Test_CommonScriptMethods, Test_ServerScriptMethods, Test_ScriptBuiltins, and Test_ScriptEntityOps as applicable.
  4. For nullable changes, run the nullability analyzers described in Nullability.md.
  5. For server/client/mapper method changes, validate the owning runtime path; do not rely only on compilation.
  6. Update ScriptMethodsMap.md when exported method files are added, removed, or materially regrouped.