Search Unity

Using network time to create a synced event

Discussion in 'Netcode for GameObjects' started by Nivagia, Dec 31, 2022.

  1. Nivagia

    Nivagia

    Joined:
    Sep 15, 2020
    Posts:
    7
    Hello,
    right now I am trying to create a syncronized event using a script from the documentation. It is the example 2 from this document
    https://docs-multiplayer.unity3d.com/netcode/current/advanced-topics/networktime-ticks/index.html

    Herethe code:

    Code (CSharp):
    1. using System.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. using UnityEngine.Assertions;
    5.  
    6. public class SyncedEventExample : NetworkBehaviour
    7. {
    8.     public GameObject ParticleEffect;
    9.  
    10.     // Called by the client to create a synced particle event at its own position.
    11.     public void ClientCreateSyncedEffect()
    12.     {
    13.         Assert.IsTrue(IsOwner);
    14.         var time = NetworkManager.LocalTime.Time;
    15.         CreateSyncedEffectServerRpc(time);
    16.         StartCoroutine(WaitAndSpawnSyncedEffect(0)); // Create the effect immediately locally.
    17.     }
    18.  
    19.     private IEnumerator WaitAndSpawnSyncedEffect(float timeToWait)
    20.     {
    21.         // Note sometimes the timeToWait will be negative on the server or the receiving clients if a message got delayed by the network for a long time. This usually happens only in rare cases. Custom logic could be implemented to deal with that scenario.
    22.         if (timeToWait > 0)
    23.         {
    24.             yield return new WaitForSeconds(timeToWait);
    25.         }
    26.  
    27.         Instantiate(ParticleEffect, transform.position, Quaternion.identity);
    28.     }
    29.  
    30.     [ServerRpc]
    31.     private void CreateSyncedEffectServerRpc(double time)
    32.     {
    33.         CreateSyncedEffectClientRpc(time); // Call a client RPC to also create the effect on each client.
    34.         var timeToWait = time - NetworkManager.ServerTime.Time;
    35.         StartCoroutine(WaitAndSpawnSyncedEffect((float)timeToWait)); // Create the effect on the server but wait for the right time.
    36.     }
    37.  
    38.     [ClientRpc]
    39.     private void CreateSyncedEffectClientRpc(double time)
    40.     {
    41.         // The owner already created the effect so skip them.
    42.         if (IsOwner == false)
    43.         {
    44.             var timeToWait = time - NetworkManager.ServerTime.Time;
    45.             StartCoroutine(WaitAndSpawnSyncedEffect((float)timeToWait)); // Create the effect on the client but wait for the right time.
    46.         }
    47.     }
    48. }
    The problem is, if I run the code on the client, then nothing happens. If I run the code on the host, I use p2p, then the event happens 2x on the host and 1x on the client.
    Maybe the comments in the code are also misleading. What am I doing wrong?
     
  2. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hi @Nivagia , could it be that this script is on an object on which you don't have ownership? By default, ServerRpcs require to be called from a NetworkObject that is owned by the client.
    So you'll get an error when trying to call this rpc as a client that is not the host.
    Adding (RequireOwnership = false) parameter to your [ServerRPC] and [ClientRPC] attributes should do the trick
     
  3. Nivagia

    Nivagia

    Joined:
    Sep 15, 2020
    Posts:
    7
    I figuered, that the example code on the unity documentation must be wrong.
    This is my code now:


    Code (CSharp):
    1. using System.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4. using UnityEngine.Assertions;
    5. using UnityEngine.Events;
    6.  
    7. public class SyncedGameStart : NetworkBehaviour
    8. {
    9.     public UnityEvent GameStart;
    10.  
    11.     // Called by the client to create a synced event at its own position.
    12.     public void ClientCreateSyncedEvent()
    13.     {
    14.         if (IsHost)
    15.         {
    16.             return;
    17.         }
    18.         var time = NetworkManager.LocalTime.Time;
    19.         CreateSyncedEventServerRpc(time);
    20.         StartCoroutine(WaitAndSpawnSyncedEvent(0));
    21.         // Create the event immediately locally.
    22.         Debug.Log("Client created synced event");
    23.     }
    24.  
    25.     private IEnumerator WaitAndSpawnSyncedEvent(float timeToWait)
    26.     {
    27.         // Note sometimes the timeToWait will be negative on the server or the receiving clients if a message got delayed by the network for a long time. This usually happens only in rare cases. Custom logic could be implemented to deal with that scenario.
    28.         Debug.Log("Time to wait is: " + timeToWait);
    29.  
    30.         if (timeToWait > 0)
    31.         {
    32.             yield return new WaitForSeconds(timeToWait);
    33.         }
    34.  
    35.         Debug.Log("Invoke GameStartNow: " + System.DateTime.Now.ToString() +":"+ System.DateTime.Now.Millisecond.ToString());
    36.         GameStart?.Invoke();
    37.     }
    38.  
    39.     [ServerRpc(RequireOwnership = false)]
    40.     private void CreateSyncedEventServerRpc(double time)
    41.     {
    42.         CreateSyncedEventClientRpc(time);
    43.         // Call a client RPC to also create the event on each client.
    44.         var timeToWait = time - NetworkManager.ServerTime.Time;
    45.         StartCoroutine(WaitAndSpawnSyncedEvent((float)timeToWait)); // Create the event on the server but wait for the right time.
    46.     }
    47.  
    48.     [ClientRpc]
    49.     private void CreateSyncedEventClientRpc(double time)
    50.     {
    51.         // The owner already created the event so skip them.
    52.         if (IsHost)
    53.         {
    54.             return;
    55.         }
    56.         var timeToWait = time - NetworkManager.ServerTime.Time;
    57.         StartCoroutine(WaitAndSpawnSyncedEvent((float)timeToWait)); // Create the event on the client but wait for the right time.
    58.     }
    59. }

    I now have the problem, that when I add a little delay to the network, the timeToWait is always a negative number and the event is triggered right away.