Objects

Overview

Networked objects are the core data model in Fusion.
Each object owns a fixed-size buffer of Words that is automatically replicated to other clients.
The SDK provides a three-level class hierarchy for different object roles.

Class Hierarchy

Class Role Description
Object Abstract base Holds Words buffer, shadow state, type information and SDK bookkeeping
ObjectRoot Top-level entity Tracks ownership, timing, scene membership and sub-objects
ObjectChild Sub-object Attached to a root; shares authority but has independent Words buffer and ObjectId

All three classes live in the SharedMode namespace.

Object (Base)

The common base class.
Holds the Words buffer, shadow state, type information and SDK bookkeeping.

C++

class Object {
public:
    ObjectId Id;                       // Globally unique identifier
    void* Engine;                      // Opaque pointer for engine integration
    ObjectType ObjectType;             // Base, Root, or Child
    bool HasValidData;                 // Words buffer contains meaningful data
    Data Header;                       // Immutable spawn data
    TypeRef Type;                      // Type hash + word count

    BufferT<Word> Shadow;              // Last-acked state (for dirty detection)
    BufferT<Word> Words;               // Current replicated state

    ObjectSpecialFlags SpecialFlags;   // IsRootTransform, etc.
    uint8_t SendFlags;                 // Current packet flags

    Tick RemoteTickSent;               // Last tick we sent
    Tick RemoteTickAcked;              // Last tick remote acked

    int32_t Status;                    // NEW / PENDING / CREATED

    NetworkedStringHeap StringHeap;    // Per-object string pool (1024 bytes)

    static constexpr size_t ExtraTailWords = sizeof(ObjectTail) / 4;  // = 6
    static constexpr double DynamicOwnerCooldownTime = 1.0 / 3;      // ~333ms

    void SetHasValidData(bool hasValidData);
    void SetSendUpdates(bool sendUpdates);

    virtual ObjectRoot* Root() = 0;

    // String heap operations
    StringHandle AddString(const PhotonCommon::CharType* str);
    const PhotonCommon::CharType* ResolveString(const StringHandle& handle, StringMessage& outStatus);
    StringHandle FreeString(const StringHandle& handle);
    bool IsValidStringHandle(const StringHandle& handle);
    uint32_t GetStringLength(const StringHandle& handle);

    // Bandwidth tracking
    uint32_t GetBytesSendLastTick() const;
    uint32_t GetBytesReceivedLastTick() const;
    uint32_t ConsumeBytesSendLastTick();        // Read and reset
    uint32_t ConsumeBytesReceivedLastTick();     // Read and reset
    void ResetReceivedBytes();
};

Key Fields

Field Type Purpose
Id ObjectId Globally unique identifier (origin player + counter)
Engine void* Opaque pointer for engine integration to store its own data
Words BufferT<Word> Current replicated state buffer
Shadow BufferT<Word> Previous acked state for change detection
Header Data Immutable spawn data sent at creation time
Type TypeRef Type hash and total word count (including tail)
HasValidData bool Whether the Words buffer contains meaningful data
Status int32_t Lifecycle status (NEW, PENDING, CREATED)
StringHeap NetworkedStringHeap Per-object string pool

ObjectRoot

A top-level networked entity.
Tracks ownership, timing, scene membership and sub-objects.

C++

class ObjectRoot final : public Object {
public:
    int LocalSendRate;                 // Per-object send rate multiplier
    double Time;                       // Network-synchronized time
    PlayerId Owner;                    // Current owner's PlayerId
    ObjectOwnerModes OwnerMode;        // Transaction, Dynamic, MasterClient, etc.
    uint32_t Scene;                    // Scene sequence this object belongs to

    ObjectOwnerIntent OwnerIntent;     // WantOwner / DontWantOwner
    double OwnerIntentCooldown;        // Time remaining before next transfer

    int32_t UpdatesReceived;           // Count of received state updates
    bool SentThisFrame;                // Whether state was sent this frame
    bool ObjectReady;                  // All required objects are present

    int32_t PluginVersion;             // Server plugin protocol version
    int32_t ClientVersion;             // Client protocol version
    int32_t ClientBaseVersion;         // Base version for delta encoding

    std::vector<ObjectId> SubObjects;  // Attached child object IDs

    static bool Is(const Object* obj);
    static ObjectRoot* Cast(Object* obj);
    static const ObjectRoot* Cast(const Object* obj);

    bool IsRequired(ObjectId id) const;
    int32_t RequiredObjectsCount() const;
    ObjectId* RequiredObjects() const;

    ObjectRoot* Root() override;       // Returns this
};

ObjectChild

A sub-object attached to a root.
Shares the root's authority but has its own independent Words buffer and ObjectId.

C++

class ObjectChild final : public Object {
public:
    ObjectId Parent;                   // Root object's ObjectId
    uint32_t TargetObjectHash;         // Hash to match child type on remote

    static ObjectId GetParent(const Object* obj);
    static bool Is(const Object* obj);
    static ObjectChild* Cast(Object* obj);
    static const ObjectChild* Cast(const Object* obj);

    ObjectRoot* Root() override;       // Navigates to parent root
};

Root() traverses up to the parent ObjectRoot, which the SDK uses for authority checks and StringHeap access.

ObjectType Enum

C++

enum class ObjectType : uint8_t {
    Base  = 1,
    Child = 2,
    Root  = 3
};

Use the static Is() and Cast() methods on ObjectRoot and ObjectChild for safe type checks rather than comparing ObjectType directly:

C++

if (ObjectRoot::Is(obj)) {
    ObjectRoot* root = ObjectRoot::Cast(obj);
    // ...
}

if (ObjectChild::Is(obj)) {
    ObjectChild* child = ObjectChild::Cast(obj);
    ObjectId parentId = child->Parent;
}

Words Buffer

The Words buffer is a flat array of int32_t values that holds all replicated property data for an object.
Properties are serialized sequentially without type markers -- the integration layer and all clients must agree on the same layout.

Region Content Notes
Words 0 .. N User properties (Prop 0, Prop 1, ... Prop N) Each property occupies a fixed number of words (e.g., float = 1, Vector3 = 3, StringHandle = 2)
Last 6 words ObjectTail Reserved for SDK use — never write to them

The total buffer size is: sum(property_words) + ExtraTailWords.

Writing and Reading

The authority client writes property values into the Words buffer during the outbound sync phase.
The SDK transmits only changed words (detected by comparing Words against Shadow).
Remote clients read from their local copy of the Words buffer during the inbound sync phase.

C++

// Authority: write a float at word offset 0
memcpy(&obj->Words.Ptr[0], &my_float, sizeof(float));

// Remote: read it back
float value;
memcpy(&value, &obj->Words.Ptr[0], sizeof(float));

Shadow Buffer

The Shadow buffer is a parallel array with the same size as Words.
After the SDK transmits an update, it compares Words against Shadow to detect which words changed and need retransmission.
After acknowledgment, Shadow is updated.

The engine integration should not modify Shadow directly.

ObjectTail

The last 6 words of every Words buffer are reserved for the SDK's internal use:

C++

#pragma pack(push, 4)
struct ObjectTail {
    int32_t RequiredObjectsCount;   // Number of required object dependencies
    uint64_t InterestKey;           // AOI interest key (8 bytes, 2 words)
    int32_t Destroyed;              // Destruction flag
    int32_t SendRate;               // Server-controlled send rate
    int32_t Dummy;                  // Reserved padding
};
#pragma pack(pop)

static_assert(sizeof(ObjectTail) == 24);  // 6 words * 4 bytes

Writing to the tail area corrupts SDK state.
The Destroyed field is especially dangerous -- overwriting it prevents proper cleanup.
The InterestKey is managed by the AOI system via SetGlobalInterestKey(), SetAreaInterestKey() and SetUserInterestKey().

When computing buffer sizes, always add Object::ExtraTailWords (6):

C++

size_t total_words = sum_of_property_words + Object::ExtraTailWords;

Status Lifecycle

Constant Value Meaning
OBJECT_STATUS_NEW 0 Just allocated, not yet sent to server
OBJECT_STATUS_PENDING 1 Sent to server, awaiting acknowledgment
OBJECT_STATUS_CREATED 2 Fully synchronized and acknowledged

Objects transition from NEW to PENDING when first included in an outgoing packet, and from PENDING to CREATED when the server acknowledges receipt.

Bandwidth Tracking

Each object tracks bytes sent and received per frame:

C++

uint32_t sent = obj->ConsumeBytesSendLastTick();      // Read + reset
uint32_t recv = obj->ConsumeBytesReceivedLastTick();   // Read + reset

These values are useful for network debugging, performance monitors and bandwidth allocation.
The "Consume" variants read the value and reset it to zero atomically.

Object Lookup

The Client maintains two maps for object lookup:

C++

// All objects (roots + children)
std::unordered_map<ObjectId, Object*>& client->AllObjects();

// Root objects only
std::unordered_map<ObjectId, ObjectRoot*>& client->AllRootObjects();

// Single-object lookup
Object* client->FindObject(ObjectId id);
ObjectRoot* client->FindObjectRoot(ObjectId id);
Object* client->FindSubObjectWithHash(ObjectRoot* root, uint32_t subObjectHash);

Object Lifecycle Summary

Step Action Description
1 Allocate Client creates object, assigns ObjectId, allocates Words/Shadow
2 Populate Integration writes initial state into Words buffer
3 Activate SetHasValidData(true) + SetSendUpdates(true)
4 Replicate Each frame: Words vs Shadow delta detection, dirty words sent
5 Ready OnObjectReady fires when object + required objects are all CREATED
6 Destroy DestroyObjectLocal() marks for removal; OnObjectDestroyed on remotes

See Object Creation for the three creation paths.

Back to top