# Server Authority + Ownership + Lifetime Flags

At some point, every multiplayer application requires the server to act as an authority on the state of a room. You may have objects that only specific users can access, physics state that you would like to enforce, or data that should be reset when a user disconnects from a room.

Traditionally this is done by writing custom server-side logic that needs to be deployed alongside the server. However, this slows iteration time significantly and requires you to redeploy your server with each update. Instead of going this route, Normcore provides an API that makes the most common server authoritative operations possible without deploying a custom server.

The two most useful tools are ownership and lifetime flags. Ownership will let you control who has the ability to modify a model and lifetime flags tell the server when a model should be automatically deleted or if it should persist between sessions.

# Ownership

Normcore uses a datastore to track all state in a room and the datastore itself is made up of model objects. Every model in the datastore, including child models, can have an owner associated with it.

By default, models have an ownerID of -1 which denotes that it has no owner. When a model is unowned, it can be modified by everyone in the room. However, when a model is owned by a client, the server will only let the owning client make changes to it or any of its child models.

# How ownership is enforced

Models can have other models as children and the ownership settings of a parent are enforced on all children. If a model is owned by one client, all other clients will be blocked from making changes to the model or any of its child models.

You can see this behavior in action with RealtimeViews and RealtimeComponents as they both use models. The RealtimeView's model contains the models for all child RealtimeViews and all RealtimeComponents, so any ownership settings on the RealtimeView will apply to child views and components as well.

The most common RealtimeView prefab is a player prefab and it's also a prime example of when to use ownership. Generally, we want to ensure that each player prefab can only be modified by the player that owns it. Rather than setting ownership of every component on the player, we can set ownership of the root RealtimeView and the server will block all other clients from making modifications to any RealtimeView or RealtimeComponent model.

Let's take a look at the hierarchy here:

Starting with the hierarchy, we have a VR Player game object at the root, and it has a child game object for the head and hands. Each game object has a RealtimeView and a RealtimeTransform and potentially another RealtimeComponent or two.

Now let's look at the models that get added to the datastore. At the root level, the VR Player's RealtimeView model is added and it contains a RealtimeAvatar and RealtimeTransform model as children. If the RealtimeView is owned by the local player, only the local player will be able to make changes to the RealtimeAvatar or RealtimeTransform components. Additionally, the RealtimeView models for the head + hands game objects are also children of the VR Player RealtimeView and so they fall under the same rules.

# Requesting ownership of a RealtimeView

# RequestOwnership() + ClearOwnership()

Let's dive into some code examples. The most straightforward methods for changing ownership are RequestOwnership() and ClearOwnership():

GameObject myObject = Realtime.Instantiate("My Object");

// Get the RealtimeView component
RealtimeView myObjectView = myObject.GetComponent<RealtimeView>();

// Mark the RealtimeView owned by the local client
myObjectView.RequestOwnership();

// Only the local client can make changes to the RealtimeView and any child views or components on myObject.

// Clear ownership of the RealtimeView
myObjectView.ClearOwnership();

// Now anyone can make changes to the RealtimeView or any child components.

It's worth noting that RequestOwnership() and ClearOwnership() take effect immediately, but if the server blocks the request (because preventOwnershipTakeover is true or the view's parent is owned by another client), then ownership and all changes that required ownership will be rolled back.

# Realtime.Instantiate() + ownedByClient

It is also possible to request ownership immediately using the ownedByClient field of Realtime.Instantiate() :

GameObject myObject = Realtime.Instantiate("My Object",
                                           ownedByClient: true);

This will ensure the RealtimeView on the root of the prefab is owned by the local client when instantiated. It's worth noting that this is equivalent:

GameObject myObject = Realtime.Instantiate("My Object",
                                           ownedByClient: false);
myObject.GetComponent<RealtimeView>().RequestOwnership();

Even though this example calls RequestOwnership() after the object is instantiated, Normcore will combine it with the instantiate call before sending. On remote clients, the RealtimeView be instantiated with this client as the owner.

# At runtime with the Unity inspector

If you're trying to debug ownership settings at runtime. The RealtimeView inspector includes Request Ownership and Clear Ownership buttons that are the same as calling RequestOwnership() or ClearOwnership() in code.

# Prevent ownership takeover of a RealtimeView

By default, anyone can request ownership of a RealtimeView at runtime and the server will approve it unless a parent RealtimeView is already owned by another client, however, this can be controlled with the preventOwnershipTakeover flag.

When this flag is true on any model, if a client attempts to change the ownership of an object, the server will deny it unless it has no owner.

# Requesting ownership of any RealtimeComponent or RealtimeModel

In addition to being able to control ownership of RealtimeView components, you can also request ownership on any RealtimeComponent or RealtimeModel in order to provide additional granularity. A single object can have more than one RealtimeComponent, all owned by different clients. Requesting ownership works just like it does for RealtimeView. Here's an example of requesting ownership on a RealtimeTransform, which is itself a RealtimeComponent subclass:

public void CreateObjectAndRequestOwnershipOfRealtimeTransform() {
    // Instantiate the My Object prefab, leave the view unowned.
    GameObject myObject = Realtime.Instantiate("My Object", ownedByClient: false);

    // Get the RealtimeTransform component
    RealtimeTransform realtimeTransform = myObject.GetComponent<RealtimeTransform>();

    // Request Ownership
    realtimeTransform.RequestOwnership();
}

However, before you can use this on a custom RealtimeComponent, you'll need to make sure your RealtimeModel includes a meta-model.

If you haven't already, it's worth reading the How Normcore Works page to fully understand the relationship between RealtimeComponent and RealtimeModel. In Normcore, all state is stored on the RealtimeModel, and ownership + lifetime flags are no different. All ownership calls to RealtimeComponent just update the state of the model to reflect the new owner. However, in order for ownership to work, you need to compile your model with a meta-model:

 





[RealtimeModel(createMetaModel: true)]
public partial class MyModel {
    [RealtimeProperty(1, true)]
    private float _myNumber;
}

The only change is the addition of createMetaModel: true in the RealtimeModel attribute constructor. This tells Normcore to compile your model with a meta-model, which is where all ownership state will be stored in order to send to the server.

# Lifetime flags

Lifetime flags tell the server how long the state for a model should be retained. Models can be set to persist between sessions, or they can be automatically deleted when the owner leaves the room or the last client leaves the room.

If you're writing a collaborative application, you may want things like objects or brush strokes to persist between sessions. However, a model that represents game state like the current score, you might want it to reset to zero after everyone leaves. If you have prefabs that represent players, you may want them to be destroyed when a player disconnects. Lifetime flags are used to denote how the server should treat each model in the datastore.

We always recommend you use lifetime flags instead of trying to write this logic yourself. If you write logic that tries to make any datastore changes when a player gets disconnected, there's no guarantee they will run successfully, especially if the user's connection is improperly terminated. However, the server will always ensure lifetime flags are honored regardless of whether a client's connection was closed properly or not.

# Instantiate a prefab that persists between sessions

Whether you're building a collaboration tool or an MMORPG, you'll likely have objects that should stick around between sessions. In Normcore, all you have to do is use the destroyWhenOwnerOrLastClientLeaves flag to denote that the object should persist. Let's take a look at an example:

public void CreatePersistentObject() {
    // Instantiate the My Object prefab and tell Normcore to let it persist even after everyone leaves the room
    GameObject myPersistentObject = Realtime.Instantiate("My Object", destroyWhenOwnerOrLastClientLeaves: false);
}

Overall, pretty simple! And the same applies to RealtimeView components in the scene. If you'd like their state to persist between sessions, just flip the "Destroy When Owner Or Last Client Leaves" option to false under Advanced Settings.

# Destroy a prefab when a client disconnects

Let's say we're going to instantiate a prefab to represent our player. In this scenario, we'll want the server to destroy it automatically if we get disconnected. We can accomplish this entirely using the optional flags in Realtime.Instantiate():

using UnityEngine;

public class MyPlayerManager : MonoBehaviour {
    private Realtime _realtime;

    private void Awake() {
        // Get the Realtime component on this game object
        _realtime = GetComponent<Realtime>();

        // Notify us when Realtime successfully connects to the room
        _realtime.didConnectToRoom += DidConnectToRoom;
    }

    private void DidConnectToRoom(Realtime realtime) {
        // Instantiate the My Player prefab for this client once we've successfully connected to the room
        Realtime.Instantiate(            "My Player",      // Prefab name
                                 ownedByClient: true,      // Make sure the RealtimeView on this prefab is owned by this client
                      preventOwnershipTakeover: true,      // Prevent other clients from calling RequestOwnership() on the root RealtimeView or any children.
            destroyWhenOwnerOrLastClientLeaves: true,      // Instruct the server to destroy this prefab when the owner (this client) disconnects. (This is true by default, but added to the example for clarity)
                                   useInstance: realtime); // Use the instance of Realtime that fired the didConnectToRoom event.
    }
}

This example gets a reference to the Realtime component and registers for the didConnectToRoom event. When we successfully connect to the room, we instantiate the "My Player" prefab to represent our local player. The prefab is set to be owned by this client, and to be destroyed when the owner, which is the local client in this case, is disconnected. For extra safety, we've also set preventOwnershipTakeover to true to prevent other clients from accidentally taking over ownership of this prefab or any of its child components or models.

And that's it! No custom logic to destroy the prefab, the server will take care of that for us and notify the remaining clients.

# What is a meta-model?

To be honest, I'm so glad you asked. I work so hard on this stuff, and if I do my job perfectly, no one will even notice. This isn't the coolest hidden gem of Normcore, but it is a neat implementation detail. (Yes, I am encouraging you to ask me more questions about how the internals of Normcore work if you ever see me on the street. Please! I never get to talk about it. There are a lot of cool details hidden in Normcore.)

Models in Normcore store state and as far as the server is concerned, it doesn't really care about what that state represents. It just needs to ensure that it is in sync with all other clients. However, there comes a time in every model's life where it has some state the server does care about. Maybe it's instructions on how to add an item to a collection, or which client can make changes to a model. This is where the meta-model comes in.

A meta-model is a child model that the server is designed to parse and use to apply custom properties to the model. In the case of ownership and lifetime flags, they're properties that exist on the meta-model that the server loads into memory and uses to accept or reject changes to the datastore.

Collections like RealtimeDictionary, RealtimeSet, RealtimeArray, and StringKeyDictionary also use the meta-model to signal to the server that they are special models that behave more like collections.

That's all folks! Hope you enjoyed this page on ownership and lifetime flags in Normcore : )