View on GitHub

FOnline Engine

Flexible cross-platform isometric game engine

Entity Model

This document explains the reusable runtime entity model: entity type descriptors, generated property accessors, prototype entities, inner-entity ownership, entity events, and the property storage model that other runtime systems build on.

Use it when changing Source/Common/Entity.*, EntityProperties.*, EntityProtos.*, Properties.*, PropertiesSerializator.*, ProtoManager.*, metadata annotations, or code that persists/synchronizes entity state.

Ownership model

The engine owns the entity runtime and metadata/property mechanics. An embedding game project owns concrete prototype files, content IDs, scripts, and gameplay rules that use those mechanics.

Keep this document focused on reusable engine behavior. Put project-specific item/critter/map/location definitions and balancing notes in the embedding project’s docs.

Runtime entity types

Source/Common/Entity.h declares the core entity taxonomy through ///@ ExportEntity annotations:

EntityTypeDesc stores metadata discovered/generated for each entity type:

For generated metadata and registration flow, see GeneratedApiAndMetadata.md.

Entity base class

Entity is the shared base for all runtime/prototype entities. It owns:

Important accessors and mutation paths include:

Do not bypass Properties when changing entity state. Property callbacks, overlay data, sync flags, persistence flags, and script-visible accessors depend on the property layer seeing the mutation.

Generated property wrappers

FO_ENTITY_PROPERTY(type, Name) expands into a small typed accessor surface:

Source/Common/EntityProperties.h defines the generated property wrapper classes:

EntityProperties itself contributes common persistent fields:

The generated wrapper classes are thin over Properties; the real storage, type information, sync/persistence flags, callbacks, and serialization decisions live in Property, Properties, and PropertyRegistrator.

Property runtime

Source/Common/Properties.h defines four central pieces:

Property flags are load-bearing:

When changing property metadata, update runtime docs and script/nullability docs together if the change affects script-visible signatures. See Nullability.md.

Base properties and overlays

A Properties instance can have base properties. This is used heavily by prototype-derived runtime entities:

This means a runtime entity is not simply a flat map from property name to value. When debugging, inspect whether the value is coming from base properties, own POD/complex storage, or overlay data.

Prototypes

Source/Common/EntityProtos.h defines prototype entities:

Source/Common/ProtoManager.* owns prototype lookup and loading:

Prototype loading is adjacent to resource baking. For baker-side proto handling, see BakingPipeline.md.

Inner entities and holders

Entities can hold other entities under named entries. Holder metadata lives in EntityTypeDesc::HolderEntryDesc:

The common persistent fields CustomHolderId and CustomHolderEntry let custom entities record holder relationships. EntityManagerApi provides custom-entity creation, lookup, and destruction hooks:

When changing holder behavior, inspect server/client entity managers and persistence paths in addition to Entity.*.

Events and time events

FO_ENTITY_EVENT(Name, Args...) creates an EntityEventWrapper member. Event callbacks are priority-ordered and return Entity::EventResult:

EntityEventWrapper::Fire() builds native call data differently for global and non-global entities: non-global events inject the entity as the first argument.

Entity::TimeEventData stores scheduled script callbacks, fire time, repeat duration, and script data. Entities that support time events are declared with the HasTimeEvents metadata flag in the ExportEntity annotations.

Serialization relationships

Entity state is serialized through property data, not by hand-copying entity fields:

Persistence backends store AnyData::Document records. For database commit/recovery details, see Persistence.md.

Tests to inspect

Relevant tests include:

Change routing

Validation checklist

  1. Build the smallest target that compiles generated entity/property code.
  2. Run entity lifecycle/prototype tests relevant to the changed type.
  3. Run property/metadata tests when property flags, registration, or serialization changes.
  4. If a property is synced, validate client/server replication paths and update Networking.md if behavior changes.
  5. If a property is persistent, validate database save/load paths and update Persistence.md if behavior changes.
  6. If a property or method is script-visible, validate generated script API and update Nullability.md where applicable.