This document is about: PUN 2
SWITCH TO

PUN Classic (v1), PUN 2 and Bolt are in maintenance mode. PUN 2 will support Unity 2019 to 2022, but no new features will be added. Of course all your PUN & Bolt projects will continue to work and run with the known performance in the future. For any upcoming or new projects: please switch to Photon Fusion or Quantum.

Ownership & Control

In PUN, networked objects are established using the PhotonView component.
Each PhotonView has a creator (instantiator), owner and controller.
In this document we will discover the definitions and concepts about PhotonView's control and ownership.
We will also list the expected behaviour in different situations and how to explicitly change ownerships of PhotonViews.

Definitions

Actor

An actor represents a client in a room.
Each actor is incrementally assigned an new number when joining a room.
The first client to join a room will be actor number 1, the second will be actor number 2 and so on.
Actors can be targets of messages and one actor per room is assigned as the master client.
Creator, Owner and Controller all are references to an actor.

PhotonView Creator

The creator of a PhotonView or the 'instantiator' (or 'spawner'), is the actor that calls PhotonNetwork.Instantiate.
This actor number is be part of the PhotonView.ViewID and is determined using ViewID / PhotonNetwork.MAX_VIEW_IDS.
The ViewID of a PhotonView does not change, so the creator ID also does not change.
In case of 'networked room objects', there is no creator (null/0) so the networked object is not created by an actor and is instead associated with the room.

PhotonView Owner

The owner of the PhotonView indicates the default Controller of the PhotonView.

In case of 'networked room objects', there is no owner (null) as the object is owned by the room and not by an actor.
In case there is an owner, if the latter is active it is also the controller.
Otherwise, the master client has control.

PhotonView Controller

The actor in control of the PhotonView (has state authority).

The owner is always the controller unless:

  • the owner is null; then the master client is the controller.
  • the owner is disconnected from the room;
    If the PlayerTTL is greater than 0, it is possible for an actor to leave the room temporarily and rejoin it later.
    While the owner is soft-disconnected, the master client becomes controller and when the owner rejoins, the owner resumes control.

photonView.IsMine indicates if the PhotonNetwork.LocalPlayer is the current Controller for this photonView.

Networked Object

A networked object is a GameObject that has a PhotonView component, including its children.
So a networked object is represented by its root PhotonView component, i.e. the PhotonView attached to the top level (root) GameObject.

Nested Networked Object

If a networked object has children GameObject in its hierarchy and one or more has a PhotonView attached then it's considered a nested networked object.
So, a nested networked object is a networked object that is part of the hierarchy of another networked object.
Usually when you instantiate a networked object that has netsted networked object(s), all PhotonViews will share the same instantiation ID (ViewID of the root PhotonView) and unless owner or controller is different, or re parented, they will have the same lifetime.

Room Object

A networked room object is a networked object that does not belong to an actor but it is a 'global networked object' belonging to the room.
It has no owner (null), nor controller (null) and it is not a result of a PhotonNetwork.Instantiate call (but could be a result of PhotonNetwork.InstantiateRoomObject).

Scene Object

A scene object is a room object that was not runtime instantiated using PhotonNetwork.InstantiateRoomObject.
It was part of the Unity scene at compile time.

Soft Disconnect

A soft disconnect is when an actor becomes inactive in a room.
It happens in case of PlayerTTL different than 0 and:

  • the client disconnects
  • the client leaves the room temporarility with the intention of coming back

The actor remains inactive until PlayerTTL expires.
If PlayerTTL < 0 or PlayerTTL == int.MaxValue the actor can remain inactive forever.

You can recover from unexpected soft disconnects using PhotonNetwork.ReconnectAndRejoin() or rejoin the same room after it happens using PhotonNetwork.RejoinRoom.
In both cases, the client needs to keep the same UserId and the actor reclaims the same actor number and all previous actor properties on rejoin.

Hard Disconnect

A hard disconnect is when an actor is completely removed from the room's actors list.

If PhotonNetwork.CurrentRoom.PlayerTtl == 0:

  • the client disconnects from Photon Server
  • the client leaves the room

If PhotonNetwork.CurrentRoom.PlayerTtl != 0:

  • the client leaves the room for good
  • PlayerTTL expires for the inactive actor

Automatic Control Transition

On Soft Disconnect

On the local client that soft-disconnected:

  • If PhotonNetwork.CurrentRoom.AutoCleanUp == true:

    • Un-parent scene objects from runtime instantiated networked objects (to prevent their destruction).

    • Any nested networked objects that were not created by this actor are unparented from parent/root object before destruction.

    • All runtime instantiated networked objects created by the disconnecting actor are destroyed.

    • Other networked objects are reset to defaults.

  • If PhotonNetwork.CurrentRoom.AutoCleanUp == false:

    • Nothing changes. You need to do manual clean up.

On remote clients -if any-:

  • Ownership unchanged for the PhotonViews previously owned by the actor that soft-disconnected.
  • Master client becomes controller of the PhotonViews owned by the actor that soft-disconnected.

On Hard Disconnect

On the local client that hard-disconnected:

  • If PhotonNetwork.CurrentRoom.AutoCleanUp == true:

    • Un-parent nested networked scene objects from disconnecting actor's instantiated networked objects (to prevent their destruction).
    • Runtime instantiated networked objects created by the disconnected actor will be destroyed.
    • Cached instantiation events and buffered RPCs are removed from relay server.
    • Scene objects are reset.
  • If PhotonNetwork.CurrentRoom.AutoCleanUp == false:

    • Nothing changes. You need to do manual clean up.

On remote clients -if any-:

  • Un-parent nested networked scene objects from disconnecting actor's instantiated networked objects (to prevent their destruction).
  • Runtime instantiated networked objects created by the disconnected actor will be destroyed.
  • Ownership reset (owner becomes null) for the remaining PhotonViews previously owned by the hard-disconnected actor.
    Those become "orphan" networked objects.
  • Master client becomes controller of these "orphan" PhotonViews, as master client is always the controller when owner == null.

On Rejoin

Player.HasRejoined == true

If the client rejoins a room that is not empty:

  • regains control of networked objects owned by the same actor.
  • [optional] Master client will resend OwnershipUpdate for any networked objects with owner != creator.

If the client rejoins a room that is still on the server but empty (EmptyRoomTTL did not expire yet):

  • regains control of networked objects owned by the same actor.
  • since it's the master client it also controls all other networked objects.

If the client rejoins a room by "resurrecting it" (reloading its state from an external source):

  • regains control of networked objects owned by the same actor.
  • since it's the master client it also controls all other networked objects.

On New Join

Player.HasRejoined == false

If the client joins a room that is not empty:

  • Nothing special.

If the client joins a room that is still on the server but empty (EmptyRoomTTL did not expire yet):

  • regains control of networked objects owned by the same actor.
  • since it's the master client it also controls all other networked objects.

Asynchronous join: if the client joins a room by "resurrecting it" (reloading its state from an external source):

  • regains control of networked objects owned by the same actor.
  • since it's the master client it also controls all other networked objects.

On Master Client Change

New master becomes controller of:

  • "orphan" networked objects: networked objects with no owner.
  • networked objects with inactive owner.

Note: If pervious master soft-disconnected, it retains ownership of any networked room object it has claimed ownership.

Explicit Ownership Transfer

You can explicitly change the owner of networked objects through its respective root PhotonView.
By default, networked objects have a fixed ownership but you can change this to allow ownership transfer either directly or via request.
Ownership change usually means controller change, unless the new owner is inactive, it will be in control of the networked object.

Ownership Transfer Options

Ownership transfer behaviour for PhotonViews is defined via OwnershipOption set in PhotonView.OwnershipTransfer.
PhotonView.OwnershipTransfer is not synchronized over the network and should not be changed once the PhotonView is instantiated.
There are three types of ownership transfer options: Fixed, Request and Takeover.
Let's find out more about each one separately.

Fixed

Ownership is fixed.
For room objects, there is no owner but the master client is the controller.
For player objects, the creator is always the owner.
This is the default value.

Request

Any actor can request ownership of PhotonViews from their current owner (or controller) when the respective OwnershipTransfer option is set to Request.

In this case, this is a two steps process: first the actor sends a request to the owner of the PhotonView, then, if the latter accepts the transfer of ownership is done explicitly by the owner and the requesting actor becomes the new owner.
The request is done via: PhotonView.RequestOwnership().

This triggers a IPunOwnershipCallbacks.OnOwnershipRequest(PhotonView targetView, Player requestingPlayer) callback on the current owner, where the developer must call targetView.TransferOwnership(requestingPlayer) to perform the actual ownership change. This allows the developer to determine in code if that request should be accepted.

Only the current owner or the current controller of the networked object are allowed to accept ownership transfer requests.

Takeover

Any actor can change the ownership of any PhotonView that has Takeover as OwnershipTransfer option.

This option is meant to directly claim ownership of PhotonViews without consent of their current owner or even attribute the PhotonView to someone else.
To take over ownership in this case simply call PhotonView.TransferOwnership(Player newOwner).
Notice that you can also change ownership to a different actor, meaning actor X can change the owner of a PhotonView set to Takeover from actor Y to actor Z.

If you call PhotonView.RequestOwnership() on a PhotonView that has Takeover as OwnershipTransfer option then the request is automatically accepted (unless someone else takes over before) without any need for callback handling.
However, in the case of Takeover ownership option it is recommended to directly call PhotonView.TransferOwnership(Player newOwner).

Renouncing Ownership

Unless the PhotonView's ownership is fixed and not meant to be changed, any actor can transfer ownership of his own PhotonViews to any other active actor.
This is done using PhotonView.TransferOwnership(Player newOwner).

PhotonView Callbacks

Ownership Change Callback

Whenever the owner of a PhotonView changes, IOnPhotonViewOwnerChange.OnOwnerChange(Player newOwner, Player previousOwner) is fired on classes that implement it and that are registered by the same respective PhotonView.

Classes implementing IOnPhotonViewOwnerChange interface need to be registered using PhotonView.AddCallbackTarget and deregistered using PhotonView.RemoveCallbackTarget.

Explicit Ownership Transfer Callbacks

There are two ownership changes callbacks in the same IPunOwnershipCallbacks interface:

  • OnOwnershipRequest(PhotonView targetView, Player requestingPlayer) when someone requests ownership from the targetView.
  • OnOwnershipTransfered(PhotonView targetView, Player previousOwner) whenever the owner changes for the targetView.

Classes implementing IPunOwnershipCallbacks interface need to be registered using PhotonNetwork.AddCallbackTarget and deregistered using PhotonNetwork.RemoveCallbackTarget.

Control Change Callback

Whenever the controller of a PhotonView changes, IOnPhotonViewControllerChange.OnControllerChange(Player newController, Player newController) is fired on classes that implement it and that are registered by the same respective PhotonView.

Classes implementing IOnPhotonViewControllerChange interface need to be registered using PhotonView.AddCallbackTarget and deregistered using PhotonView.RemoveCallbackTarget.

Network Destroy Callback

Sometimes you want to be notified of when a networked object is about to be destroyed.
There is a callback for this which also fires on all PhotonViews of the same networked object: the main/root PhotonView and all its nested ones -if any-.

Whenever PhotonNetwork.Destroy is called, and just before finishing the network destruction, IOnPhotonViewPreNetDestroy.OnPreNetDestroy(PhotonView rootView) is fired on classes that implement it and that are registered by the same respective PhotonView.

Classes implementing IOnPhotonViewPreNetDestroy interface need to be registered using PhotonView.AddCallbackTarget and deregistered using PhotonView.RemoveCallbackTarget.

Back to top