FishNet icon indicating copy to clipboard operation
FishNet copied to clipboard

SyncVar not properly syncing NetworkObject and NetworkBehaviour

Open Danon5 opened this issue 2 years ago • 3 comments

General Unity version: 2022.3.11f1 Fish-Networking version: Tested with custom repro projects on 4.0.2 and 3.11.7 Discord link: https://discord.com/channels/424284635074134018/923983677980045382/1174415887269380186

Description If an ObserverCondition is "hiding" multiple objects from the client, and some of those objects have SyncVar references to each other via synced NetworkObject or NetworkBehaviours, they will often not be synced once the ObserverCondition returns true and reveals them.

Replication Steps to reproduce the behavior:

  1. Set up a basic server / client connection system.
  2. Create a custom timed ObserverCondition, make it return false by default, but let there be some way to make it start returning true during runtime (I use a button with a static boolean).
  3. Each time a client finishes loading the ready scenes, spawn them a Player prefab with a script on it. The script should have a SyncVar<NetworkObject> in it. The client should have authority over its own object, but I don't think that's relevant.
  4. In OnStartServer of the Player script, spawn any other network object and set the value of the SyncVar to this newly spawned object.
  5. Add some logs into the OnChanged callback of the SyncVar, or just log the SyncVar's value in Update.
  6. Start the server, and connect 2 clients. The clients will only receive information about their owned object.
  7. Use the method you devised to cause the ObserverCondition to start returning true.
  8. Observe the logs and notice how the non-host client will often not have the SyncVar value set. It might take a couple of attempts of starting the server + connecting to get this to happen.

Expected behavior I expect that the SyncVar should consistently be a non-null value.

Repro Package ReproPackage.zip

I included a zipped .unitypackage file. All you would have to do is create a new project on my Unity version (or a similar one, presumably), install FishNet 4.0.2 into the project, then import the .unitypackage. I use Debug.LogError to log information so that you can build the project and test by launching multiple executables if you do not want to use something like ParrelSync. I am personally building the project, so to recreate the behavior, it might be worth building instead of using multiple editors. Build in Development Mode so that you can see Debug.LogErrors.

You want to start 2 instances of the built project, and on one press Start Server and then Start Client (to effectively create a host client). Each time you press Start Client on an instance, you will see that client's ID appear above the buttons if the connection was successful. In the development console, you will see logs about the NetworkObject and string SyncVars getting set.

You then want to press Start Client on the other instance. You will again see logs about the NetworkObject and SyncVars being set.

Lastly, you want to press Enable Observer Condition on Client 0. This will cause the two clients to see attempt to sync each other's player objects. Here, you want to look at Client 1 (the second instance) and see if there is NO new NetworkObject SyncVar log. If there is a new log about the NetworkObject being successfully set, just try again until you don't see it (just close the executables and reopen them, should only take a second). If there is no log, that means the bug has happened and the SyncVar has failed to sync when the ObserverCondition started returning true. You would then have to manually click the Force Mark SyncVar Dirty on Client 0 to see the log appear, as that will ensure that it's synced.

Danon5 avatar Nov 15 '23 22:11 Danon5

This is a known limitation because the value does not actually change on the SyncVar, so the server does not send any changes. I've personally made some considerations on resolving this in the future but it won't be until later in v4.

FirstGearGames avatar Nov 18 '23 13:11 FirstGearGames

Whoops, did not mean to press close on the issue.

Anyways, is that limitation you described actually what's going on here? Because it seems to send SyncVars properly when the objects are received by the ObserverCondition, such as strings, ints, etc. Sometimes it also works properly for NetworkObjects. The problem seems to be the execution order. Like I described, if a client receives and initial SyncVar state saying that a NetworkObject SyncVar should be the object with ID 1, for example, it currently doesn't ensure that NetworkObject 1 is actually spawned yet, I think. It would explain why it works sometimes and not other times.

Danon5 avatar Nov 18 '23 20:11 Danon5

Its because the SyncVar holds a reference to an object which is not spawned yet, so it's populated with default. It doesn't automatically get the proper reference when the object does spawn in later. This is something I've been wanting to resolve but it's a tricky scenario given the current setup of things.

If you are certain the object being referenced that is null for the client is spawning at the same time you can give the missing object a lower initialize order on the NetworkObject, this will ensure it spawns first.

FirstGearGames avatar Nov 21 '23 00:11 FirstGearGames

There is unfortunately no simple way to go about this without a massive rework so this will have to be the way for an unknown amount of time.

FirstGearGames avatar Apr 04 '24 00:04 FirstGearGames