Ownership

Overview

Every networked object in Fusion has exactly one owner at any time.
The owner is the client that has write authority over the object's Words buffer -- only the owner's outbound state updates are accepted by the server.
Ownership determines who can modify replicated properties, and the ownership model is selected per-object at creation time.

Modes

This describes Shared-Authority mode (SimulationMode::Shared), where each owner writes its own objects.
In Client-Server mode (SimulationMode::Authority) state authority belongs to the simulation server, and a PlayerPredicted object instead designates a predicting player that holds input authority — see Modes and Topologies and the Predicted Simulation section below.

Owner Modes

C++

enum class ObjectOwnerModes : uint8_t {
    Transaction     = 0,
    PlayerAttached  = 1,
    Dynamic         = 2,
    MasterClient    = 3,
    GameGlobal      = 4,
    PlayerPredicted = 5,
};
Mode Behavior Use Case
Transaction Ownership requires explicit request/release. One owner at a time with cooldown. Shared resources, vehicles, interactables
PlayerAttached Permanently owned by the creating player. Cannot be transferred. Player avatars, player-specific state
Dynamic Any client can claim ownership instantly. Built-in cooldown prevents oscillation. Physics objects, loose items
MasterClient Always owned by the master client. Transfers automatically on master migration. Game state, scoreboards, match logic
GameGlobal Server/plugin-owned global object. No client can claim ownership. Server-authoritative state
PlayerPredicted Server is the authority; a designated player feeds inputs and predicts state locally. Mispredictions are corrected via OnPredictionOverride / OnPredictionReset. Used in SimulationMode::Authority. Authoritative simulation, anti-cheat-sensitive gameplay

SanitizeOwnerMode

C++

ObjectOwnerModes Client::SanitizeOwnerMode(ObjectOwnerModes ownerMode) const;

Validates and normalizes an owner mode value.
Call this if the mode comes from user input or configuration to ensure it maps to a valid enum value.

Querying Ownership

C++

// Returns the PlayerId of the current owner
PlayerId Client::GetOwner(const Object* obj);

// Returns true if the local client is the owner
bool Client::IsOwner(const Object* obj);

// Returns true if the local client can write to the object
bool Client::CanModify(const Object* obj);

// Returns true if the object has any owner (not unowned)
bool Client::HasOwner(const Object* obj) const;

IsOwner() and CanModify() both check local authority, but CanModify() may account for additional conditions beyond basic ownership.

Requesting Ownership

Expressing Intent

C++

void Client::SetWantOwner(Object* obj);      // Signal desire to own
void Client::SetDontWantOwner(Object* obj);   // Release ownership intent

These methods set the object's ObjectOwnerIntent:

C++

enum class ObjectOwnerIntent : uint8_t {
    DontWantOwner = 0,
    WantOwner     = 1,
};

Transaction Mode

In Transaction mode, ownership transfer is a coordinated process:

  1. Client A calls SetWantOwner(obj).
  2. If the object is unowned, Client A becomes the owner immediately.
  3. If Client B currently owns the object, the request is queued until Client B calls SetDontWantOwner(obj).
  4. After transfer, a cooldown prevents immediate re-transfer.

Dynamic Mode

In Dynamic mode, any client can take ownership by calling SetWantOwner().
Unlike Transaction, the transfer happens without waiting for the current owner to release.
A configurable cooldown prevents rapid ownership bouncing:

C++

client->SetDynamicOwnerCooldown(1.0 / 3);   // default ~333ms
double cooldown = client->GetDynamicOwnerCooldown();

The older static constant Object::DynamicOwnerCooldownTime has been removed; configure the cooldown at runtime via Client::SetDynamicOwnerCooldown(seconds).

Clearing Cooldown

C++

void Client::ClearOwnerCooldown(Object* obj);

Resets the per-object ownership transfer cooldown.

Responding to Requests

C++

void Client::RespondToOwnershipRequest(Object* obj, PlayerId requester, bool granted);

When another player requests ownership of an object the local client owns, the request surfaces on the OnOwnershipRequest broadcaster (described below).
The owner answers it asynchronously by calling RespondToOwnershipRequest, passing the requester PlayerId from the callback and granted = true to transfer ownership or granted = false to deny it.
Passing the requester back explicitly means concurrent requests from different players are each answered to the correct target.
The response is validated and forwarded by the plugin and surfaces on the requester via OnOwnershipResponse.

MasterClient Mode

Objects with MasterClient mode are always owned by the master client:

C++

constexpr PlayerId MasterClientPlayerId = 0xFFFF;  // UINT16_MAX

When the master client disconnects, the Photon server assigns a new master.
Objects with MasterClient mode automatically transfer to the new master.

Send Rate Control

Ownership and send rate work together for bandwidth optimization.

Per-Object Local Send Rate

C++

void Client::SetLocalSendRate(Object* obj, uint32_t sendRate);

Sets the local send rate divisor for an object.
A value of 1 means the object sends every tick (highest rate).
A value of 16 means it sends every 16th tick (lowest rate).
This is a client-side optimization -- the object still exists on all clients but consumes less bandwidth when you are not the active authority.

Server-Side Send Rate

C++

void Client::SetRoomSendRate(const Object* obj, int32_t sendRate);
void Client::ResetRoomSendRate(const Object* obj);

SetRoomSendRate writes to the RoomSendRate field in the ObjectTail, which the server uses for bandwidth allocation. ResetRoomSendRate clears it back to the default. Both replace the older SetSendRate / ResetSendRate (which wrote to the now-renamed SendRate tail field).

The local divisor counterpart is SetLocalSendRate(obj, rate) / ResetLocalSendRate(obj).

For session-wide tuning (rather than per-object), Client::SetAuthoritySendRate(rate) controls the send rate used while operating in SimulationMode::Authority.

Ownership + Send Rate Pattern

A common pattern when requesting/releasing ownership:

C++

// Requesting ownership: send every tick
client->SetLocalSendRate(obj, 1);
client->SetWantOwner(obj);

// Releasing ownership: minimize bandwidth
client->SetLocalSendRate(obj, 16);
client->SetDontWantOwner(obj);

This ensures that when you own an object, you send updates at full rate, and when you release it, you minimize bandwidth until someone else claims it.

Ownership Callbacks

OnObjectOwnerChanged

C++

Broadcaster<void(ObjectRoot*)> OnObjectOwnerChanged;

Fires when ownership of any object changes.
The ObjectRoot has its Owner field already updated to the new owner's PlayerId.
Integration layers typically:

  1. Check if the local client is now the owner (IsOwner(obj)).
  2. Enable or disable write access to the object's synchronizer.
  3. Emit an engine-side signal so game logic can react.

OnPredictionOverride

C++

Broadcaster<void(ObjectRoot*)> OnPredictionOverride;

Fires when the authoritative state overrides local prediction (renamed from OnObjectPredictionOverride).
This happens with ObjectOwnerModes::PlayerPredicted objects when the server's authoritative state diverges from the predicted local state.
Use this for visual correction or interpolation reset.

OnPredictionReset

C++

Broadcaster<void(ObjectRoot*)> OnPredictionReset;

Fires when the server rejects a predicted input sequence — the prediction queue is reset and re-applied from the latest authoritative state.

OnOwnershipRequest

C++

Broadcaster<void(ObjectRoot*, PlayerId requester)> OnOwnershipRequest;

Fires on the current owner when another player requests ownership.
The handler answers asynchronously by calling Client::RespondToOwnershipRequest(obj, requester, granted) — see Responding to Requests above.
The reply is forwarded by the plugin via RPC_InternalOwnershipResponse and surfaces on the requester through OnOwnershipResponse.

OnOwnershipResponse

C++

Broadcaster<void(ObjectRoot*, bool granted)> OnOwnershipResponse;

Fires on the requester after the current owner replies to an ownership request.

Predicted Simulation

Setting Client::SetPredictingPlayer(root, player) on a PlayerPredicted-mode object designates which player is feeding inputs:

C++

// On the authority: designate which player feeds inputs.
client->SetPredictingPlayer(root, predictingPlayer);

uint32_t seq    = client->GetInputSequence(root);
bool     hasIa  = client->HasInputAuthority(root);
bool     amIp   = client->IsPredictingPlayer(root);

// On the predicting player: queue an input frame each tick.
root->QueueInput(deltaSeconds, std::move(payload));

// On the authority: apply the queued inputs and write the resulting state.
root->ExecuteInputs(deltaSeconds);

QueueInput runs on the client that holds input authority; ExecuteInputs runs on the simulation server, which applies the queued inputs and writes the authoritative state.
Queued inputs also surface on the authority through the OnInput broadcaster.
Inputs travel as RPC_InternalInput messages.
The server may reject sequences that arrive out of order; the SDK then fires OnPredictionReset so the client can replay from the new baseline.

Special Player IDs

C++

constexpr PlayerId MasterClientPlayerId  = 0xFFFF;       // Master client
constexpr PlayerId PluginPlayerId        = 0xFFFE;       // Server plugin
constexpr PlayerId ObjectOwnerPlayerId   = 0xFFFD;       // "Send to object owner"

Note that PlayerId is now uint16_t (was uint32_t in older SDKs); the sentinel constants moved from the 0xFFFFFFFF family to the 0xFFFF family.

Sub-Object Authority

Sub-objects (ObjectChild) share their root object's authority.
There is no independent ownership for children -- whoever owns the root owns all of its sub-objects:

C++

ObjectRoot* ObjectChild::Root() override;

Ownership queries on a child should go through the root:

C++

ObjectRoot* root = client->GetRoot(childObj);
bool owned = client->IsOwner(root);

Common Mistakes

Mistake Symptom
Calling SetWantOwner on a PlayerAttached object No effect; ownership cannot transfer
Writing Words without ownership Data overwritten by authority on next update
Not adjusting send rate when taking ownership Wasted bandwidth at low send rate
Ignoring OnPredictionOverride / OnPredictionReset Visual snapping when authority corrects state
Not handling master client migration MasterClient-mode objects stop updating
  • {VersionPath}/manual/object-creation -- ObjectOwnerModes passed at creation time
  • {VersionPath}/manual/objects -- Object hierarchy and data model
  • {VersionPath}/manual/interest-area -- Interest keys interact with ownership
  • {VersionPath}/manual/time -- When ownership callbacks fire in the frame loop
Back to top