Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
* [Distributed authority WebGL quickstart](learn/distributed-authority-webgl.md)
* [Networking concepts](networking-concepts.md)
* [Authority](terms-concepts/authority.md)
* [Ownership](terms-concepts/ownership.md)
* [Network topologies](network-topologies.md)
* [Network topologies](terms-concepts/network-topologies.md)
* [Client-server](terms-concepts/client-server.md)
* [Listen server host architecture](learn/listenserverhostarchitecture.md)
* [Distributed authority topologies](terms-concepts/distributed-authority.md)
* [Ownership](terms-concepts/ownership.md)
* [Configuration](configuration.md)
* [Configuring connections](configure-connections.md)
* [Connection approval](basics/connection-approval.md)
Expand All @@ -23,6 +23,7 @@
* [Network components](network-components.md)
* [Core components](components/core/corecomponents.md)
* [NetworkObject](components/core/networkobject.md)
* [NetworkObject ownership](advanced-topics/networkobject-ownership.md)
* [NetworkObject parenting](advanced-topics/networkobject-parenting.md)
* [NetworkBehaviour](components/core/networkbehaviour.md)
* [Synchronizing & Order of Operations](components/core/networkbehaviour-synchronize.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# NetworkObject ownership

Before reading these docs, familiarize yourself with the concepts of [ownership](../terms-concepts/ownership.md) and [authority](../terms-concepts/authority.md) within Netcode for GameObjects. It's also recommended to read the documentation on the [NetworkObject](../components/core/networkobject.md).

To see if the local client is the owner of a NetworkObject, you can check the[`NetworkObject.IsOwner`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.IsOwner.html) or the [`NetworkBehaviour.IsOwner`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.IsOwner.html) property.

To see if the server owns a NetworkObject, you can check the [`NetworkObject.IsOwnedByServer`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.IsOwnedByServer.html) or the [`NetworkBehaviour.IsOwnedByServer`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkBehaviour.IsOwnedByServer.html) property.

> [!NOTE]
> When you want a specific NetworkObject to keep existing after the owner leaves the session, you can set the `NetworkObject.DontDestroyWithOwner` property to `true`. This ensures that the owned NetworkObject isn't destroyed as the owner leaves.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some suggested adjustments:
To assure a spawned NetworkObject persists after the owner leaves a session, set the NetworkObject.DontDestroyWithOwner property to true. This assures the client-owned NetworkObject doesn't get destroyed when the owning client leaves.


## Authority ownership

If you're creating a client-server game and you want a client to control more than one NetworkObject, or if you're creating a distributed authority game and the authority/current owner of the object would like to change ownership, use the following ownership methods.
Comment on lines +12 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ownership vs authority

The client-server and distributed authority network topologies have slight differences when it comes to ownership and authority.

  • Client-server: A client can own a spawned NetworkObject but the server always maintains spawn authority over the NetworkObject. Ownership only provides the owning client authority over NetworkVariables (with write permissions), any NetworkTransform configured for owner authority, and can be used to send an RPC that targets the owner.
  • Distributed authority: If a client owns a NetworkObject it becomes the "over-all" authority of that NetworkObject. This means the owning client has spawn/de-spawn authority, write permissions to NetworkVariables (associated with the owned NetworkObject), the motion authority (if the NetworkObject has any NetworkTransforms), and the owning client can change the ownership permissions flags.

The following methods change their behavior based on the network topology being used:


The default `NetworkObject.Spawn` method will set server-side ownership when using a client-server topology. When using a distributed authority topology, this method will set the client who calls the method as the owner.

```csharp
GetComponent<NetworkObject>().Spawn();
```
Comment on lines +16 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkObject.Spawn

  • Client-Server: The server defaults as the owner of this spawned object.
  • Distributed authority: The client invoking this method becomes the owner of the spawned object.


To change ownership, the authority uses the `ChangeOwnership` method:

```csharp
GetComponent<NetworkObject>().ChangeOwnership(clientId);
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkObject.SpawnWithOwnership

  • Client-Server: The client identifier passed in as a parameter defines who the owner will be.
  • Distributed authority: It is not recommended to use this method when using distributed authority if there are initial settings you want applied when spawning the new instance. For this scenario, it is recommended to use NetworkObject.Spawn, allow the settings to be applied locally, and then use NetworkObject.ChangeOwnership.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved SpawnWithOwnership to the bottom of the page. That way ChangeOwnership has a higher prominence than SpawnWithOwnership, hopefully leading to fewer SpawnAuthority issues.

## Client-server ownership

In a client-server game, to give ownership back to the server use the `RemoveOwnership` method:

```csharp
GetComponent<NetworkObject>().RemoveOwnership();
```

> [!NOTE]
> `RemoveOwnership` isn't supported when using a [distributed authority network topology](../../terms-concepts/distributed-authority.md).

## Distributed authority ownership

When building a distributed authority game, it is important to remember that owner of an object is always the authority for that object. As the simulation is shared between clients, it is important that ownership can be easily passed between game clients. This enables:

1. Evenly sharing the load of the game simulation via redistributing objects whenever a player joins or leaves.
2. Allowing ownership to be transferred immediately to a client in situations where a client is interacting with that object and so wants to remove lag from those interactions.
3. Controlling when and object is safe and/or valid to be transferred.

The authority of any object can always change ownership as outlined in [authority ownership](#authority-ownership).

### Ownership permissions settings

The following ownership permission settings, defined by [`NetworkObject.OwnershipStatus`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.OwnershipStatus.html), control how ownership of objects can be changed during a distributed authority session:

| **Ownership setting** | **Description** |
|-------------------|-------------|
| `None` | Ownership of this NetworkObject can't be redistributed, requested, or transferred (a Player might have this, for example). |
| `Distributable` | Ownership of this NetworkObject is automatically redistributed when a client joins or leaves, as long as ownership is not locked or a request is pending. |
| `Transferable` | Any client can change ownership of this NetworkObject at any time, as long as ownership is not locked or a request is pending. |
| `RequestRequired` | Ownership of this NetworkObject must be requested before ownership can be changed. |
| `SessionOwner` | This NetworkObject is always owned by the [session owner](distributed-authority.md#session-ownership) and can't be transferred or distributed. If the session owner changes, this NetworkObject is automatically transferred to the new session owner. |

You can also use `NetworkObject.SetOwnershipLock` to lock and unlock the permission settings of a NetworkObject for a period of time, preventing ownership changes on a temporary basis.

```csharp
// To lock an object from any ownership changes
GetComponent<NetworkObject>().SetOwnershipLock(true);

// To unlock an object so that the underlying ownership permissions apply again
GetComponent<NetworkObject>().SetOwnershipLock(false);
```

### Changing ownership in distributed authority

When a NetworkObject is set with `OwnershipPermissions.Transferable` any client can change ownership to any other client using the `ChangeOwnership` method:

```csharp
GetComponent<NetworkObject>().ChangeOwnership(clientId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkObject.ChangeOwnership(clientId);


// To change ownership to self
GetComponent<NetworkObject>().ChangeOwnership(NetworkManager.Singleton.LocalClientId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkObject.ChangeOwnership(NetworkManager.LocalClientId);

```

When a non-authoritative game client calls `ChangeOwnership`, the ownership change can fail. On a failed attempt to change ownership, the `OnOwnershipPermissionsFailure` callback will be invoked with a [`NetworkObject.OwnershipPermissionsFailureStatus`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.OwnershipPermissionsFailureStatus.html) to give information on the failure.

```csharp
internal class MyTransferrableBehaviour : NetworkBehaviour
{
public override void OnNetworkSpawn()
{
NetworkObject.OnOwnershipPermissionsFailure = OnOwnershipPermissionsFailure;
base.OnNetworkSpawn();
}

private void OnOwnershipPermissionsFailure(NetworkObject.OwnershipPermissionsFailureStatus ownershipPermissionsFailureStatus)
{
// Called on the calling client when NetworkObject.ChangeOwnership() has failed
}

public override void OnNetworkDespawn()
{
NetworkObject.OnOwnershipPermissionsFailure = null;
base.OnNetworkDespawn();
}
}
Comment on lines +85 to +103
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public class ChangeOwnershipBehaviour : NetworkBehaviour
{
private Action OwnershipChanged;
public override void OnNetworkSpawn()
{
NetworkObject.OnOwnershipPermissionsFailure += OnOwnershipPermissionsFailure;
base.OnNetworkSpawn();
}

public void TakeOwnership(Action ownershipChanged)
{
    if (OwnershipChanged != null)
    {
        Debug.LogWarning($"Ownership request already sent!");
        return;
    }
    OwnershipChanged = ownershipChanged;
    NetworkObject.ChangeOwnership(NetworkManager.LocalClientId);
}

protected override void OnOwnershipChanged(ulong previous, ulong current)
{
    if (current == NetworkManager.LocalClientId)
    {
        // If this is invoked, then the ownership change succeeded
        try
        {
            OwnershipChanged?.Invoke();
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }
        OwnershipChanged = null;
    }
    base.OnOwnershipChanged(previous, current);
}

/// <summary>
/// Invoked on the calling client when NetworkObject.ChangeOwnership() has failed
/// </summary>
/// <param name="ownershipPermissionsFailureStatus">Why the change in ownership failed.</param>
private void OnOwnershipPermissionsFailure(NetworkObject.OwnershipPermissionsFailureStatus ownershipPermissionsFailureStatus)
{
    OwnershipChanged = null;
    // Make any adjustments or provide player feedback based on why the change in ownership failed.
    switch (ownershipPermissionsFailureStatus)
    {
        case NetworkObject.OwnershipPermissionsFailureStatus.NotTransferrable:
            {
                break;
            }
        case NetworkObject.OwnershipPermissionsFailureStatus.RequestInProgress:
            {
                break;
            }
        case NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly:
            {
                break;
            }
        case NetworkObject.OwnershipPermissionsFailureStatus.RequestRequired:
            {
                break;
            }
        case NetworkObject.OwnershipPermissionsFailureStatus.Locked:
            {
                break;
            }
    }
}

public override void OnNetworkDespawn()
{
    NetworkObject.OnOwnershipPermissionsFailure += OnOwnershipPermissionsFailure;
    base.OnNetworkDespawn();
}

}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want all this detailed code here? Maybe it'd be more useful to have the most simple example here and a more fully featured "putting it all together" section at the end?

```

### Requesting ownership in distributed authority

When a NetworkObject is set with `OwnershipPermissions.RequestRequired` any client can request the ownership for themselves using the `RequestOwnership` method:

```csharp
var requestStatus = GetComponent<NetworkObject>().RequestOwnership();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var requestStatus = NetworkObject.RequestOwnership();

```

The `RequestOwnership` will return an [`OwnershipRequestStatus`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.OwnershipRequestStatus.html) to indicate the initial status of the request. To view the result of the request, `OnOwnershipRequestResponse` callback will be invoked with a [`NetworkObject.OwnershipRequestResponseStatus`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.OwnershipRequestResponseStatus.html).

```csharp
internal class MyRequestableBehaviour : NetworkBehaviour
{
public override void OnNetworkSpawn()
{
NetworkObject.OnOwnershipRequestResponse = OnOwnershipRequestResponse;
base.OnNetworkSpawn();
}

private void OnOwnershipRequestResponse(NetworkObject.OwnershipRequestResponseStatus ownershipRequestResponseStatus)
{
// Called when the requesting client has gotten a response to their request
}

public override void OnNetworkDespawn()
{
NetworkObject.OnOwnershipRequestResponse = null;
base.OnNetworkDespawn();
}
}
Comment on lines +117 to +135
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

///


/// When a NetworkObject's permissions includes the
/// flag.
///

public class RequestableOwnershipBehaviour : NetworkBehaviour
{
public override void OnNetworkSpawn()
{
NetworkObject.OnOwnershipRequestResponse += OnOwnershipRequestResponse;
base.OnNetworkSpawn();
}

private void OnOwnershipRequestResponse(NetworkObject.OwnershipRequestResponseStatus ownershipRequestResponseStatus)
{
    switch (ownershipRequestResponseStatus)
    {
        case NetworkObject.OwnershipRequestResponseStatus.Approved:
            {
                // OnOwnershipChanged and OnGainedOwnership is invoked on the client requesting side.
                // OnOwnershipChanged and OnLostOwnership is invoked on the previous owning client side.
                break;
            }
        case NetworkObject.OwnershipRequestResponseStatus.RequestInProgress:
            {
                // Another client already has a request pending.
                break;
            }
        case NetworkObject.OwnershipRequestResponseStatus.CannotRequest:
            {
                // Cannot request means RequestRequired is no longer a permission of this NetworkObject.
                // The owning client changed while the request was being sent.
                break;
            }
        case NetworkObject.OwnershipRequestResponseStatus.Denied:
            {
                // The owning client denied the request.
                break;
            }
        case NetworkObject.OwnershipRequestResponseStatus.Locked:
            {
                // Ownership has been locked by the owning client.
                break;
            }
    }
}

public override void OnNetworkDespawn()
{
    NetworkObject.OnOwnershipRequestResponse -= OnOwnershipRequestResponse;
    base.OnNetworkDespawn();
}

}

```

## Spawn with ownership

To spawn a `NetworkObject` that is [owned](../../terms-concepts/ownership.md) by a different game client than the one doing the spawning, use the following:

```csharp
GetComponent<NetworkObject>().SpawnWithOwnership(clientId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkObject.SpawnWithOwnership(clientId);

```

> [!NOTE]
> Using `SpawnWithOwnership` can result in unexpected behaviour when the spawning game client makes any other changes on the object immediately after spawning.

Using `SpawnWithOwnership` and then editing the object locally will mean the client doing the spawning will behave as the "spawn authority". The spawn authority will have limited local [authority](../terms-concepts/authority.md) over the object, but will not have [ownership](../terms-concepts/ownership.md) of the object that is spawned. This means any owner-specific checks during the spawn sequence will not be invoked on the spawn authority side.

Any time you would like to spawn an object for another client and then immediately make adjustments on that object, it's instead recommended to use `Spawn`. After adjusting, the spawn authority can immediately follow with a call to `ChangeOwnership`.

```csharp
[Rpc(SendTo.Authority)]
void SpawnForRequestingPlayer(RpcParams rpcParams = default) {
var instance = Instantiate(myprefab).GetComponent<NetworkObject>();
instance.Spawn();
instance.transform.position = transform.position;
instance.GetComponent<MyNetworkBehaviour>.MyNetworkVar.Value = initialValue;
instance.ChangeOwnership(rpcParams.Receive.SenderClientId)
}
```

This flow allows the spawning client to completely spawn and finish initializing the object locally before transferring the ownership to another game client.
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# NetworkObject parenting

### Overview
## Overview

If you aren't completely familiar with transform parenting in Unity, then it's highly recommended to [review over the existing Unity documentation](https://docs.unity3d.com/Manual/class-Transform.html) before reading further to properly synchronize all connected clients with any change in a GameObject component's transform parented status, Netcode for GameObjects requires that the parent and child GameObject components have NetworkObject components attached to them.
If you aren't completely familiar with transform parenting in Unity, then it's highly recommended to [review over the existing Unity documentation](https://docs.unity3d.com/Manual/class-Transform.html) before reading further. To properly synchronize all connected clients with any change in a GameObject component's transform parented status, Netcode for GameObjects requires that the parent and child GameObject components have NetworkObject components attached to them. Otherwise, you can use the [AttachableBehaviour](../components/helper/attachablebehaviour.md) and [AttachableNode](../components/helper/attachablenode.md) helper components to synchronize other types of parenting.

### Parenting rules
## Parenting rules

- Setting the parent of a child's `Transform` directly (that is, `transform.parent = childTransform;`) always uses the default `WorldPositionStays` value of `true`.
- It's recommended to always use the `NetworkObject.TrySetParent` method when parenting if you plan on changing the `WorldPositionStays` default value.
- Likewise, it's also recommended to use the `NetworkObject.TryRemoveParent` method to remove a parent from a child.
- When a server parents a spawned NetworkObject component under another spawned NetworkObject component during a Netcode game session this parent child relationship replicates across the network to all connected and future late joining clients.
- When an [authority](../terms-concepts/authority.md) parents a spawned NetworkObject component under another spawned NetworkObject component during a Netcode game session this parent child relationship replicates across the network to all connected and future late joining clients.
- If, while editing a scene, you place an in-scene placed NetworkObject component under a GameObject component that doesn't have a NetworkObject component attached to it, Netcode for GameObjects preserves that parenting relationship.
- During runtime, this parent-child hierarchy remains true unless the user code removes the GameObject parent from the child NetworkObject component.
- **Note**: Once removed, Netcode for GameObjects won't allow you to re-parent the NetworkObject component back under the same or another GameObject component that with no NetworkObject component attached to it.
Expand Down Expand Up @@ -38,12 +38,16 @@ virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { }
> [!NOTE] Multi-generation children and scale
> If you are dealing with more than one generation of nested children where each parent and child have scale values other than `Vector3.one`, then mixing the `WorldPositionStays` value when parenting and removing a parent will impact how the final scale is calculated! If you want to keep the same values before parenting when removing a parent from a child, then you need to use the same `WorldPositionStays` value used when the child was parented.

### Only a server (or a host) can parent NetworkObjects
### Who can parent NetworkObjects

Similar to [Ownership](../basics/networkobject#ownership), only the server (or host) can control NetworkObject component parenting.
By default, only the [authority](../terms-concepts/authority.md) of an object can change the parenting of the object. This means in a client-server game, only the server (or host) can control NetworkObject component parenting. In a distributed authority game the owner of the object can always parent the object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

owner of the object can always parent the object under any other spawned NetworkObject (owned or not).


To allow the [owner](../terms-concepts/ownership.md) to parent their owned object in a client-server game, use the [`NetworkObject.AllowOwnerToParent`](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkObject.html#Unity_Netcode_NetworkObject_AllowOwnerToParent) property.

![image](../images/networkobject/allowOwnerToParent.png)

> [!NOTE]
> If you run into a situation where your client must trigger parenting a NetworkObject component, one solution is for the client to send an RPC to the server. Upon receiving the RPC message, the server then handles parenting the NetworkObject component.
> If you run into a situation where your client must trigger parenting a NetworkObject component, one solution is for the client to send an RPC to the authority. Upon receiving the RPC message, the authority then handles parenting the NetworkObject component.

### Only parent under a NetworkObject Or nothing (root or null)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ When using a [server authoritative networking model](../terms-concepts/authority

To spawn a network prefab, you must first create an instance of the network prefab and then invoke the spawn method on the NetworkObject component of the instance you created. In most cases, you will want to keep the NetworkObject component attached to the root GameObject of the network prefab.

By default, a newly spawned network prefab instance is owned by the authority unless otherwise specified.

See [Ownership](networkobject.md#ownership) for more information.
See [NetworkObject ownership](../advanced-topics/networkobject-ownership.md) for more information.

The following is a basic example of how to spawn a network prefab instance:

Expand Down
Loading