Search Unity

Question GetComponent<NetworkObject>().SpawnWithOwnership(OwnerClientId) Not working

Discussion in 'Netcode for GameObjects' started by GuirieSanchez, Nov 8, 2022.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    Hi, I'm trying to spawn an object (that I call "gameManager") for each client that joins the server. Through the PlayerObject on each client (including the host), I instantiate and subsequently spawn this "gameManager" through a ServerRpc call. My goal is to have these newly spawned gameManagers to Destroy themselves via

    Code (CSharp):
    1. public override void OnNetworkSpawn()
    2.     {
    3.         base.OnNetworkSpawn();
    4.         if (!IsOwner) Destroy(this);
    5.     }
    if they do not belong to the PlayerObject / client.

    Therefore, I used the following code in order to assign ownership over them,
    Code (CSharp):
    1. gameManager.GetComponent<NetworkObject>().SpawnWithOwnership(OwnerClientId);
    so that the
    (!IsOwner)
    check will detect that they belong to a certain client (the one that requested the spawn to the server), and only that copy doesn't get destroyed (or its script in this case).

    However, even by doing this, the only "OwnerClientID" that takes effect is that of the server/host, meaning that all the copies of the gameManager GameObject won't be destroyed on the host, while all the copies of the gameManager on each client will be destroyed.

    Here's a picture of the NetworkObject component of 2 copies of the gameManager. As you can see, their OwnerClientId is 0 for both, when it should be 0 for one of them (the host) and 1 for the other (the client).


    upload_2022-11-8_15-37-13.png
    and
    upload_2022-11-8_15-37-30.png

    Here's also the script that I'm running to spawn the gameManagers:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Netcode;
    5. using UnityEngine.SceneManagement;
    6. using TMPro;
    7.  
    8. public class PlayerConnectionScript : NetworkBehaviour
    9. {
    10.  
    11.     [SerializeField] private GameObject gameManagerPrefab;
    12.     [SerializeField] private GameObject canvasPrefab;
    13.     [SerializeField] private TextMeshProUGUI _myText;
    14.  
    15.  
    16.     private void Awake()
    17.     {
    18.         DontDestroyOnLoad(this.gameObject);
    19.         NetworkManager.Singleton.SceneManager.OnLoadComplete += OnLoadComplete;
    20.     }
    21.     private void OnDisable()
    22.     {
    23.         NetworkManager.Singleton.SceneManager.OnLoadComplete -= OnLoadComplete;
    24.     }
    25.     private void OnLoadComplete(ulong clientId, string sceneName, LoadSceneMode loadSceneMode)
    26.     {
    27.         if (IsLocalPlayer)
    28.         {
    29.             SpawnGameManagerServerRpc(OwnerClientId);
    30.         }
    31.     }
    32.  
    33.     void Start()
    34.     {
    35.  
    36.         if (IsServer) NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback;
    37.         if (!IsLocalPlayer) return;
    38.    
    39.  
    40.         if (IsLocalPlayer && IsHost)
    41.         {
    42.             UpdateTextClientRpc("HOST");
    43.         }
    44.  
    45.     }
    46.     private void OnClientConnectedCallback(ulong obj)
    47.     {
    48.         if (IsServer)
    49.         {
    50.             UpdateTextClientRpc("CLIENT");
    51.         }
    52.     }
    53.  
    54.  
    55.     [ClientRpc]
    56.     private void UpdateTextClientRpc(string text)
    57.     {
    58.         _myText.text = text;
    59.     }
    60.  
    61.  
    62.     [ServerRpc(RequireOwnership = false)] //If I don't use RequireOwnership I get the ServerRpc error
    63.     //[ServerRpc]
    64.     private void SpawnGameManagerServerRpc(ulong Id)
    65.     {
    66.  
    67.         GameObject gameManager = Instantiate(gameManagerPrefab);
    68.         gameManager.GetComponent<NetworkObject>().SpawnWithOwnership(Id);
    69.  
    70.  
    71.         GameObject canvas = Instantiate(canvasPrefab);
    72.         canvas.GetComponent<NetworkObject>().SpawnWithOwnership(Id);
    73.  
    74.     }
    75.  
    76.  
    77. }
    78.  
     
    Last edited: Nov 8, 2022
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    You're not supposed to Destroy() network objects!
    If the "this" object is a NetworkBehaviour script or a GameObject with a NetworkObject on it, this should actually throw an error.

    Anyhow, there's a simple solution to this: have a single GameManager. There is no need to make separate (networked) objects on a per-client basis.

    Instead, you write your "GameManager" code so it branches on checks like IsOwner or IsServer and sometimes looking up the clientId to determine what the player owns, controls, or is allowed to do.

    Or you write separate component scripts like GameManagerClient, GameManagerOwner and GameManagerServer that you put on the same object, and then upon spawn you enable/disable these based on the IsClient/IsOwner/IsServer flags. But the game object itself is the same for all clients and the server.
     
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    It is but it doesn't throw an error, hmm

    Before it was like this, but I wasn't able to use RPCs unless it was a Networkbehaviour. Also, if it's not spawned by a PlayerObject, then I won't be able to use
    IsClient/IsOwner/IsServer
    flags since it doesn't have any info about who's who, right?
     
  4. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    452
    I'm struggling to get the checks and the ownership even when I'm spawning the "GameManager" with ownership from the server, I can't imagine how to do these checks having the "GameManager" being a non-network object. Could you please elaborate on how to do it?
     
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,975
    Make a prefab GameManager with a NetworkObject and your NetworkBehaviour derived script on it and place it in the scene. You can then use this to „manage“ all game networking stuff and any non networked local scripts hook into it with events or similar (ie SendMessage or ScriptableObject variables or ….).