Ownership and Authority

Spatial's networking system is based on distributed authority topology. This means that final authority over the state of an object is distributed across clients in the space based on object ownership.

Each object in the shared virtual scene has a owner actor. The owner actor is responsible for controlling the object and its local state is considered the source of truth. By default, the creator of an object is the owner, however ownership can be taken over by other actors or transferred if it is permitted.

Checking for Authority

isMine

In Network Behaviours, you can easily check if the current client is the owner with the isMine property. In most cases you will want to use hasControl instead.

hasControl

It's possible for an object to have no owner, in which case the current master client is given authority by default. Because of this, it's recommended to use hasControl instead of isMine in cases where you want a controlling behaviour to execute at all times.

Customizing behavior based on Authority

A common pattern with distributed authority is to have the authoritative client run additional logic to control game state or enforce rules.

public class MonsterAI : SpatialNetworkBehaviour, IOwnershipChanged
{
private NavMeshAgent _agent;
private float _agentDecisionTimer;

public override void Spawned()
{
if (hasControl)
{
// Controlling client will initialize the monster's AI here
}
}

public void OnOwnershipChanged(NetworkObjectOwnershipChangedEventArgs args)
{
// We got control of the object, possible because the previous owner disconnected
if (hasControl)
{
_agentDecisionTimer = 0.0f;
}
}

private void Update()
{
if (hasControl)
{
_agentDecisionTimer += Time.deltaTime;
if (_agentDecisionTimer > 1.0f)
{
_agentDecisionTimer = 0.0f;

// Move monster towards the closest player
IActor closestActor = SpatialBridge.actorService.actors.Values
.OrderBy(actor => Vector3.Distance(actor.avatar.position, transform.position))
.FirstOrDefault();
_agent.SetDestination(closestActor.avatar.position);
}
}
}
}
public class MonsterAI : SpatialNetworkBehaviour, IOwnershipChanged
{
private NavMeshAgent _agent;
private float _agentDecisionTimer;

public override void Spawned()
{
if (hasControl)
{
// Controlling client will initialize the monster's AI here
}
}

public void OnOwnershipChanged(NetworkObjectOwnershipChangedEventArgs args)
{
// We got control of the object, possible because the previous owner disconnected
if (hasControl)
{
_agentDecisionTimer = 0.0f;
}
}

private void Update()
{
if (hasControl)
{
_agentDecisionTimer += Time.deltaTime;
if (_agentDecisionTimer > 1.0f)
{
_agentDecisionTimer = 0.0f;

// Move monster towards the closest player
IActor closestActor = SpatialBridge.actorService.actors.Values
.OrderBy(actor => Vector3.Distance(actor.avatar.position, transform.position))
.FirstOrDefault();
_agent.SetDestination(closestActor.avatar.position);
}
}
}
}

Fixed Ownership

Some objects can be created that have their ownership set to a fixed actor. This is the case for player avatars where we never want another client to take control of the object.

Unless AllowOwnershipTransfer is enabled for an object, fixed ownership is assumed.

Allow Ownership Transfer Setting

You can check if ownership can be transferred for any object through the ISpaceObject interface. For NetworkObjects you can access it through SpatialNetworkObject.spaceObject.canTakeOwnership property. For other objects, you can use the ISpaceContentService to get the ISpaceObject instance.

Distributing Authority

Because authority defaults to the master client when an object has no owner, it potentially centralizes the additional performance cost onto a single client. If that client is a low-powered device or it is on a poor network connection, it may affect the quality of the experience for other clients too. This is especially true when your experience has a lot of entities such as enemies with AI behaviour or physics objects.

One way to mitigate this is to distribute authority evenly across actors in the server instance. This can be done by transferring ownership of objects to other clients based on certain conditions. For example, you could transfer ownership of a physics object to the client that is closest to it, or to the client that has the most enemies in its vicinity.