I’m having trouble syncronizing changes made in scenes when they are being loaded by the clients. Everything that happens between the Load event and LoadComplete event is not being synced to the loading client. If there’s a change afterwards it gets resynced.
I’m delaying the load completition by setting allowSceneActivation to false to make a fade out and that increases the time it which this desync can happen. I have tried taking that code out but, it only changes the window of the problem, it still exists.
Is there a way to force a synchronization like it happens when a clients joins?
Ok, could you describe the kinds of changes being made during the Load and LoadComplete events on the server side and are these changes being applied to assets loaded by the scene? Are you using Single or Additve mode scene loading? What is your Load Scene Time Out value set to in your NetworkManager and does the time it takes for the server to load the scene, disable the allowSceneActivation (assuming this is done on the SceneEventTypes.Load event)?
The more details (i.e. including script segments and or files) the faster I can figure out what is happening.
I am attaching a project that will handle an “intermission” scene between scene load and scene load complete: SceneIntermissionProject.zip (106.8 KB)
Here is a video of the project running:
When you first open the project, you will want to load the BootStrapScene and then take a look at the ExtendedNetworkManager:
This has the SceneLoader component that includes a “Use Intermission” flag to enable/disable the intermission part. It also handles loading the scenes.
Next you will want to look at the SceneIntermission component:
This handles the whole allowSceneActivation part while also synchronizing clients with progress of loading and such.
Finally, you will want to look at the GenericBall prefab (what spawns when you hit the space bar) to see how that is handled… it basically will push the spawned instance to the DDOL if spawned while in the middle of a scene intermission (take note of this part).
So… when you are spawning things during the “scene intermission” time frame the instances will still land in the “to be unloaded” scene that is the currently active scene when spawning things… but that scene is about to be unloaded and as such the spawned instances are migrated into the DDOL along with having additional settings to keep them synchronized with the currently active scene.
My guess is you have close to the right solution and just need to review the project I provided above and see if there is anything you might be doing differently (if so make that adjustment in your project).
If you find that there is something completely different that you are doing, then if you could either provide that information and/or modify the project included and send back the zip. If you do this, only include only the same folders as were in the above zip file attached in the new zip file (keeps the size much smaller) and attach that here so I can see what is going on?
Otherwise, please do let me know if providing you with this project helps you resolve your issue?
Thank you for you response and sorry for my lack of detail.
The problem is not when the server/host loads the scene but, when the client does it. The error occurs on both single or additive scenes and is not related to spawning objects but to moving them, network variables have been updating correctly so far.
I was able to reproduce the problem in the project you sent: SceneIntermissionProject.zip (109.9 KB)
I have made a cube, that exists on the scenes, and I move it when the client is loaded. That code is in ActionMovement and in SceneIntermission, in the new OnClientLoadEvent method. I have also made the client to wait 3 seconds before loading the scene to increase the time span in which the error occurs.
Ahh nice! Ok, so this helped clarify that you want to halt all scene loading for all clients and the host/server which means you are halting the final loading of the scene for everyone.
I ran across some issues in the pattern in the modifications you applied.
ActionMovement
I added some additional logging information to show the order of operations for the in-scene placed Cube object.
The OnNetworkSpawn and then Start method is invoked when the in-scene placed cube is instantiated as displayed in the screenshot below.
The screenshot is once I entered into a state where the scene was ready to be loaded but I haven’t pressed the proceed button.*
The green rectangular areas show that:
The 1st scene’s cube ran through the instantiation and spawn process.
This means at that time frame the 1st scene cube had registered for the OnClientLoadingScene event.
The red rectangular areas show that:
The new scene is basically loaded into memory, but nothing has been run through the final load instantiation phase (requires allowSceneActivation to be set to true).
The cube from the 1st scene has been despawned.
Double note that it has not been destroyed, so this cube instance’s ActionMovement component is still registered for the OnClientLoadingScene event.
At this point you have the new scene “almost loaded” with no instances of anything from that new scene and you have the current scene (to be unloaded) that has despawned everything but has yet to destroy anything on the server side).
But the biggest blocker here is:
The server halts loading of the new scene until it gets a notification from the clients that they are loading the scene.
The server doesn’t send out the scene loading notification to clients until it is done loading the scene.
Refer back to top and loop that logic indefinitely.
So, I think using allowSceneActivation under this context (i.e. server and clients are all using it for the same scene load event) isn’t going to yield the desired results.
Really… I think there are two separate tasks here:
Synchronize when the clients finalize their scene loading relative to when all other clients have reported “being ready to finalize loading”.
I adjusted the scene intermission to handle this.
Assure there are certain values set prior to sending/serializing the scene load synchronization message.
If you are really just looking to position things with a NetworkTransform then you can:
Change the ActionMovement to derive from NetworkTransform and just apply any offsets within OnNetworkSpawn prior to invoking base.OnNetworkSpawn.
This will make adjustments to the transform before the NetworkTransform generates its first synchronization message.
If you want to synchronize anything else you might want to set, then override OnSynchronize and serialize information there.
This is invoked whenever the NetworkObject and the associated NetworkBehaviours are serialized (i.e. anything you add to the serialization stream is applied before spawning on clients).
Here is the project with most of the modifications mentioned above: SceneIntermission2.zip (112.0 KB)
I modified the following:
SceneIntermission.cs:
This now only registers clients for the scene load event when there is an intermission in progress and de-registers when it is complete.
Clients report when they have loaded the scene up to the point where things would be instantiated and the other scene would be unloaded.
When all clients have finished, the server sends a notification to all clients to proceed.
ActionMovement.cs
Now derives from NetworkTransform and the Cube just has the ActionMovement component.
This allows you to have the server apply adjustments before the NetworkTransform gets serialized and sent with the scene load message to clients.
It includes a new property “MoveWhenSpawned” which I have enabled in the modified project.
If you disable it, you will see that it will not work the way I think you want this to work.
I understand that when moving the object on, OnNetworkSpawn, gets the desired result of the movement being synchronized. But, that’s not something I can do.
The players can move objects anytime, even the host can do it. From what I’m seeing, the only way to make it work would be to register all those movements and then reaply them? Or to disable the interactivity until all clients have loaded.
Would you mind providing the details as to why you cannot do this?
The players can move objects anytime, even the host can do it. From what I’m seeing, the only way to make it work would be to register all those movements and then reaply them? Or to disable the interactivity until all clients have loaded.
You might need to either submit a bug via the editor so we can look at your project =or= you can provide as much detail as possible (and/or modify the project provided so that it replicates your issue exactly).
Currently, it sounds like you want to have some form of “fog of war” kind of setup where players can move things around but they aren’t able to see other player movements until the end? It isn’t really clear as to what kinds of objects the players can move, whether they can only be owned by clients or if they can be owned by anyone, and it is a bit confusing as to why you want to move something that has yet to be loaded and instantiated as an actual object in the engine (i.e. using the allowSceneActivation means there are no GameObject instances of anything loaded in the “almost loaded” scene yet… but, from what I can tell so far, you want to move things that have yet to even be instantiated…which you can’t do in a single player game either).
Perhaps you could also provide more details on the logical game flow you are wanting to achieve as well? (the more details the better)
It’s a VR application where any user can move objects by grabbing them, by physic interacions… There is no fog of war, the scene haulting just occurs for the clients, it is done to make a fade out-in transition between scenes.
During the transition time, other users can interact with the world and move objects. That’s when the desync happens, as shown in my first reply, the network transforms don’t get synced correctly between different users. If any other movement is applied to those objects later, the issue fixes.
I don’t think saving the positions of all objects and then moving them to a new position and then back to their correct position is a solution. It would work, but its not something acceptable for the user.
The issue was replicated in that first answer when the cube was moved while the client was loading the scene.
NetworkSceneManager will wait for the server to load the scene before sending the Load scene event.
This is because the server needs to load the assets first in order to send the assets’ serialized data.
If the server is blocked from doing this manually or for some period of pre-defined time, then it only will delay when the server sends the Load scene event.
What I would do if all you need is a fade out and fade in scene is to:
Use additive loading to load and unload scenes.
Additively load a separate UI fade in/out scene. (could be loaded at the very beginning of the session or could be loaded and the Canvas with the UI elements are migrated into the DDOL scene).
When you want to transition, you would invoke a method on a NetworkBehaviour attached to the UI elements canvas that would start the fade out process and send an RPC to clients to begin…or have a NetworkVariable that holds the state of the UI element (i.e. networked state machine…you could just use enums as your states) it to fade out (to whatever color or image).
Then you load the next scene (with the actual assets) additively.
Upon being loaded you would then fade back in.
If you are loading scenes using LoadSceneMode.Single and none of the possible solutions thus far work for your project’s needs, I would recommend using additive scene loading.
Additive scene loading does require you to handle the unloading of scenes you no longer need or want hanging around, but it also provides you with the flexibility to maintain one scene as active during a new scene load event while also providing you the convenience of handling when you want to switch the newly loaded scene over to be active.
Alternately, you could have a “general” scene (loaded additively or single) that is always the active scene during a network session and is always where newly instantiated and dynamically spawned NetworkObjects will reside. The “general” scene might have some managers (i.e. like the fade-in and fade-out UI) or the like in it but would not contain things like in-scene placed NetworkObjects and/or level geometry etc. You would just load the scenes with in-scene placed NetworkObjects and/or level geometry (etc) additively until you want to switch to a new scene (or new content)…which then anything spawned remains in the “general” scene even when additively loading a new content scene.
Start of session:
General-Scene (Active scene)
Everything instantiated and spawned dynamically will reside in this scene.
It is never unloaded during the session.
UI Scene Fade in/out could be located here too.
Scene-ContentTypeA (additively loaded)
The actual assets/content for the first “level” or “starting content”.
Load new scene content - phase 1: (loading)
General-Scene (Active scene)
Everything remains instantiated and spawned in this scene.
UI Scene Fades out
Scene-ContentTypeA (Remains additively loaded)
The actual assets/content for the first “level” or “starting content”.
Scene-ContentTypeB (Additively loading)
Load new scene content - phase 2: (loaded)
General-Scene (Active scene)
Everything remains instantiated and spawned in this scene.
UI Scene Fades in
Scene-ContentTypeA (Unloaded via scene event Unload)
Scene-ContentTypeB (Additively loaded)
New content is loaded
(There are some additional phases in the above, but it is the general idea)
Based on what you have described, I would say using an additive scene loading would be the better approach to resolve your issue.