fix: soft synchronization issues can be avoided if object creation and destruction messages are held until player has completely loaded scene#531
Conversation
This fixes two issues: It prevents the postprocessing method ProcessScene from being run during scene transitions while in PlayMode within the editor. This fixes the issue where the transport is no longer being polled for events upon shutting down the server,host, or client.
This prevents this post processing code from being executed when in PlayMode (editor and stand alone build)
This ended up not being needed in the end. Reverting back to the original file.
Removing the commented out OnDisable function (the containing code was moved into the Shutdown method) and removed the Awake method (the containing code was moved into the Init method).
This hadn't fully registered the merge.
Just getting the footprint ready and made some adjustments to the ground and wall materials.
WIP files for the scene transition samples.
Refactoring some comments
| public static void ProcessScene() | ||
| { | ||
| //If we are in playmode (editor or stand alone) we do not want this to execute | ||
| if (Application.isPlaying) |
There was a problem hiding this comment.
I wonder why the code before skipped out in this case. Any recollections @TwoTenPvP ?
There was a problem hiding this comment.
This has been added as a fix by Noel but with the new better fix isn't necessary anymore I think.
| /// </summary> | ||
| public class NetworkClient | ||
| { | ||
| public bool IsClientDoneLoadingScene; |
There was a problem hiding this comment.
Please comment public's
There was a problem hiding this comment.
+1 to Matt's point.
I'd also suggest naming it as IsLoadingScene too (and reverse the flag being false by default).
Fixing a merge artifact...
| if(createPlayerObject) | ||
| { | ||
| //Provide meaningful feedback to developers that this happened. | ||
| NetworkLog.LogWarning("NetworkManager configured to spawn player prefab but no prefab is defined or there is a problem with the prefab hash."); |
There was a problem hiding this comment.
This is interesting. Is this different behavior than now, where if you don't have your default player spawn it crashes? Now you get a warning?
|
|
||
| //Reset our observed objects list | ||
| //[NSS]: This assures we have an up to date list of all observed objects | ||
| m_ObservedObjects.Clear(); |
There was a problem hiding this comment.
I wonder why m_ObservedObjects is not a local to HandleApproval? It's only used in HA, and only in the approved state, and in that state, it is unconditionally cleared, then built up and read from.
Due to name changes, the default player prefab was removed from the NetworkManager.
|
I also wonder how this relates to #493 |
| //If we are in playmode (editor or stand alone) we do not want this to execute | ||
| if (Application.isPlaying) | ||
| { | ||
| return; | ||
| } |
There was a problem hiding this comment.
can this change go as an "unfix" for #493 on itself in isolation?
| if (PendingClients.ContainsKey(clientId)) | ||
| PendingClients.Remove(clientId); | ||
| NetworkClient client = new NetworkClient() | ||
| { | ||
| IsClientDoneLoadingScene = false, | ||
| ClientId = clientId, | ||
| }; |
There was a problem hiding this comment.
| if (PendingClients.ContainsKey(clientId)) | |
| PendingClients.Remove(clientId); | |
| NetworkClient client = new NetworkClient() | |
| { | |
| IsClientDoneLoadingScene = false, | |
| ClientId = clientId, | |
| }; | |
| if (PendingClients.ContainsKey(clientId)) | |
| { | |
| PendingClients.Remove(clientId); | |
| } | |
| var client = new NetworkClient | |
| { | |
| IsLoadingScene = true, | |
| ClientId = clientId, | |
| }; |
| } | ||
|
|
||
| //Create the new player from the assigned prefab | ||
| NetworkObject netObject = NetworkSpawnManager.CreateLocalNetworkObject(false, 0, (playerPrefabHash == null ? NetworkConfig.PlayerPrefabHash.Value : playerPrefabHash.Value), null, position, rotation); |
There was a problem hiding this comment.
| NetworkObject netObject = NetworkSpawnManager.CreateLocalNetworkObject(false, 0, (playerPrefabHash == null ? NetworkConfig.PlayerPrefabHash.Value : playerPrefabHash.Value), null, position, rotation); | |
| var networkObject = NetworkSpawnManager.CreateLocalNetworkObject(false, 0, (playerPrefabHash == null ? NetworkConfig.PlayerPrefabHash.Value : playerPrefabHash.Value), null, position, rotation); |
| /// GetStreamBufferFrameCount | ||
| /// Returns how many frames have been processed (Inbound/Outbound) | ||
| /// </summary> | ||
| /// <param name="queueType"></param> | ||
| /// <returns>number of frames procssed</returns> | ||
| public uint GetStreamBufferFrameCount(RpcQueueHistoryFrame.QueueFrameType queueType) | ||
| { | ||
| return queueType == RpcQueueHistoryFrame.QueueFrameType.Inbound ? m_InboundFramesProcessed : m_OutboundFramesProcessed; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// AddToInternalMLAPISendQueue | ||
| /// NSS-TODO: This will need to be removed once we determine how we want to handle specific | ||
| /// internal MLAPI commands relative to RPCS. | ||
| /// Example: An network object is destroyed via server side (internal mlapi) command, but prior to this several RPCs are invoked for the to be destroyed object (Client RPC) | ||
| /// If both the DestroyObject internal mlapi command and the ClientRPCs are received in the same frame but the internal mlapi DestroyObject command is processed prior to the | ||
| /// RPCs being invoked then the object won't exist and additional warnings will be logged that the object no longer exists. | ||
| /// The vices versa scenario (create and then RPCs sent) is an unlikely/improbable scenario, but just in case added the CreateObject to this special case scenario. | ||
| /// | ||
| /// To avoid the DestroyObject scenario, the internal MLAPI commands (DestroyObject and CreateObject) are always invoked after RPCs. | ||
| /// </summary> | ||
| /// <param name="queueItem">item to add to the internal MLAPI queue</param> | ||
| public void AddToInternalMLAPISendQueue(RpcFrameQueueItem queueItem) | ||
| internal void AddToInternalMLAPISendQueue(RpcFrameQueueItem queueItem) |
There was a problem hiding this comment.
changes in this file are looking more like refactorings, should probably go into another isolated PR (and maybe not even targeting release branch IMHO)
| //In case there is no Singleton, then just exit | ||
| if(!NetworkManager.Singleton) | ||
| { | ||
| return; | ||
| } |
There was a problem hiding this comment.
why? if there is no NetworkManager.Singleton, we are probably in trouble already, right?
| return; | ||
| } | ||
|
|
||
| bool AdvanceFrameHistory = false; |
There was a problem hiding this comment.
I believe local variables should be "camelCase" as it used to be...
| bool AdvanceFrameHistory = false; | |
| bool advanceFrameHistory = false; |
| public void ProcessSendQueue() | ||
| internal void ProcessSendQueue() |
There was a problem hiding this comment.
why all these public to internal changes? the class itself is already internal, there is no extra protection coming from all these public to internal changes anyway?
| { | ||
| var PoolStream = queueItem.NetworkBuffer; | ||
| if (NetworkManager.Singleton.IsListening) | ||
| List<RpcFrameQueueItem> CompletedRpcQueueItems = new List<RpcFrameQueueItem>(); |
There was a problem hiding this comment.
| List<RpcFrameQueueItem> CompletedRpcQueueItems = new List<RpcFrameQueueItem>(); | |
| var completedRpcQueueItems = new List<RpcFrameQueueItem>(); |
| { | ||
| switch (queueItem.QueueItemType) | ||
| var clientIds = new List<ulong>(queueItem.ClientNetworkIds); | ||
| var PoolStream = queueItem.NetworkBuffer; |
There was a problem hiding this comment.
| var PoolStream = queueItem.NetworkBuffer; | |
| var poolStream = queueItem.NetworkBuffer; |
| internal void InternalMessagesSendAndFlush() | ||
| { | ||
| foreach (RpcFrameQueueItem queueItem in m_InternalMLAPISendQueue) | ||
| if(NetworkManager.Singleton && NetworkManager.Singleton.IsListening) |
There was a problem hiding this comment.
again, similar to above, I'm not sure if this check is really needed. maybe IsListening but not sure if Singleton check is required here. also, why we're checking if we're IsListening here? shouldn't the call-site of InternalMessagesSendAndFlush method do that check first before calling this method in the first place? I have questions... :P
| var advanceFrameHistory = false; | ||
| //In case there is no Singleton, then just exit | ||
| if(!NetworkManager.Singleton) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var AdvanceFrameHistory = false; |
There was a problem hiding this comment.
the same pattern followed here, see my comments above ^^^
| //Right before the server sends the switch scene message, it marks the client it is sending the message to as having not loaded the scene being switched to | ||
| NetworkManager.Singleton.ConnectedClientsList[j].IsClientDoneLoadingScene = false; |
There was a problem hiding this comment.
huh? why? what's going on here? :D
| m_EditorVersion: 2019.4.17f1 | ||
| m_EditorVersionWithRevision: 2019.4.17f1 (667c8606c536) | ||
| m_EditorVersion: 2019.4.20f1 | ||
| m_EditorVersionWithRevision: 2019.4.20f1 (6dd1c08eedfa) |
| "dependencies": { | ||
| "com.unity.collab-proxy": "1.2.16", | ||
| "com.unity.ide.rider": "1.1.4", | ||
| "com.unity.ide.vscode": "1.2.3", | ||
| "com.unity.multiplayer.mlapi": "file:../../com.unity.multiplayer.mlapi", | ||
| "com.unity.package-validation-suite": "0.19.2-preview", | ||
| "com.unity.test-framework": "1.1.19", | ||
| "com.unity.textmeshpro": "2.1.1", | ||
| "com.unity.timeline": "1.2.17", | ||
| "com.unity.ugui": "1.0.0", | ||
| "com.unity.modules.ai": "1.0.0", | ||
| "com.unity.modules.androidjni": "1.0.0", | ||
| "com.unity.modules.animation": "1.0.0", | ||
| "com.unity.modules.assetbundle": "1.0.0", | ||
| "com.unity.modules.audio": "1.0.0", | ||
| "com.unity.modules.cloth": "1.0.0", | ||
| "com.unity.modules.director": "1.0.0", | ||
| "com.unity.modules.imageconversion": "1.0.0", | ||
| "com.unity.modules.imgui": "1.0.0", | ||
| "com.unity.modules.jsonserialize": "1.0.0", | ||
| "com.unity.modules.particlesystem": "1.0.0", | ||
| "com.unity.modules.physics": "1.0.0", | ||
| "com.unity.modules.physics2d": "1.0.0", | ||
| "com.unity.modules.screencapture": "1.0.0", | ||
| "com.unity.modules.terrain": "1.0.0", | ||
| "com.unity.modules.terrainphysics": "1.0.0", | ||
| "com.unity.modules.tilemap": "1.0.0", | ||
| "com.unity.modules.ui": "1.0.0", | ||
| "com.unity.modules.uielements": "1.0.0", | ||
| "com.unity.modules.umbra": "1.0.0", | ||
| "com.unity.modules.unityanalytics": "1.0.0", | ||
| "com.unity.modules.unitywebrequest": "1.0.0", | ||
| "com.unity.modules.unitywebrequestassetbundle": "1.0.0", | ||
| "com.unity.modules.unitywebrequestaudio": "1.0.0", | ||
| "com.unity.modules.unitywebrequesttexture": "1.0.0", | ||
| "com.unity.modules.unitywebrequestwww": "1.0.0", | ||
| "com.unity.modules.vehicles": "1.0.0", | ||
| "com.unity.modules.video": "1.0.0", | ||
| "com.unity.modules.vr": "1.0.0", | ||
| "com.unity.modules.wind": "1.0.0", | ||
| "com.unity.modules.xr": "1.0.0" | ||
| }, | ||
| "testables": [ | ||
| "com.unity.multiplayer.mlapi" | ||
| ] | ||
| } | ||
|
No newline at end of file |
||
| "dependencies": { | ||
| "com.unity.collab-proxy": "1.2.16", | ||
| "com.unity.ide.rider": "1.1.4", | ||
| "com.unity.ide.vscode": "1.2.3", | ||
| "com.unity.multiplayer.mlapi": "file:../../com.unity.multiplayer.mlapi", | ||
| "com.unity.package-validation-suite": "0.19.2-preview", | ||
| "com.unity.test-framework": "1.1.22", | ||
| "com.unity.textmeshpro": "2.1.1", | ||
| "com.unity.timeline": "1.2.17", | ||
| "com.unity.ugui": "1.0.0", | ||
| "com.unity.modules.ai": "1.0.0", | ||
| "com.unity.modules.androidjni": "1.0.0", | ||
| "com.unity.modules.animation": "1.0.0", | ||
| "com.unity.modules.assetbundle": "1.0.0", | ||
| "com.unity.modules.audio": "1.0.0", | ||
| "com.unity.modules.cloth": "1.0.0", | ||
| "com.unity.modules.director": "1.0.0", | ||
| "com.unity.modules.imageconversion": "1.0.0", | ||
| "com.unity.modules.imgui": "1.0.0", | ||
| "com.unity.modules.jsonserialize": "1.0.0", | ||
| "com.unity.modules.particlesystem": "1.0.0", | ||
| "com.unity.modules.physics": "1.0.0", | ||
| "com.unity.modules.physics2d": "1.0.0", | ||
| "com.unity.modules.screencapture": "1.0.0", | ||
| "com.unity.modules.terrain": "1.0.0", | ||
| "com.unity.modules.terrainphysics": "1.0.0", | ||
| "com.unity.modules.tilemap": "1.0.0", | ||
| "com.unity.modules.ui": "1.0.0", | ||
| "com.unity.modules.uielements": "1.0.0", | ||
| "com.unity.modules.umbra": "1.0.0", | ||
| "com.unity.modules.unityanalytics": "1.0.0", | ||
| "com.unity.modules.unitywebrequest": "1.0.0", | ||
| "com.unity.modules.unitywebrequestassetbundle": "1.0.0", | ||
| "com.unity.modules.unitywebrequestaudio": "1.0.0", | ||
| "com.unity.modules.unitywebrequesttexture": "1.0.0", | ||
| "com.unity.modules.unitywebrequestwww": "1.0.0", | ||
| "com.unity.modules.vehicles": "1.0.0", | ||
| "com.unity.modules.video": "1.0.0", | ||
| "com.unity.modules.vr": "1.0.0", | ||
| "com.unity.modules.wind": "1.0.0", | ||
| "com.unity.modules.xr": "1.0.0" | ||
| }, | ||
| "testables": [ | ||
| "com.unity.multiplayer.mlapi" | ||
| ] | ||
| } |
There was a problem hiding this comment.
this is probably due to the engine version upgrade, please leave this change out...
| public String GetSceneNameLinkedToState(GlobalGameState.GameStates gameState, int currentindex = 0) | ||
| { | ||
| var results = m_StateToSceneList.Where(entry => entry.stateToLoadScene == gameState); | ||
| if (results != null && ( (results.Count() - 1) >= currentindex) ) | ||
| { | ||
| return results.ElementAt(currentindex).SceneToLoad; | ||
| } | ||
| return String.Empty; | ||
| } |
There was a problem hiding this comment.
| public String GetSceneNameLinkedToState(GlobalGameState.GameStates gameState, int currentindex = 0) | |
| { | |
| var results = m_StateToSceneList.Where(entry => entry.stateToLoadScene == gameState); | |
| if (results != null && ( (results.Count() - 1) >= currentindex) ) | |
| { | |
| return results.ElementAt(currentindex).SceneToLoad; | |
| } | |
| return String.Empty; | |
| } | |
| public string GetSceneNameLinkedToState(GlobalGameState.GameStates gameState, int currentindex = 0) | |
| { | |
| var results = m_StateToSceneList.Where(entry => entry.stateToLoadScene == gameState); | |
| if (results != null && ((results.Count() - 1) >= currentindex)) | |
| { | |
| return results.ElementAt(currentindex).SceneToLoad; | |
| } | |
| return string.Empty; | |
| } |
|
Closing this fix because it needs to be refactored to only fix the soft synch issue when:
|
This is a fix for soft synchronization issues when a client is transitioning to a new or existing in game session.
What this fix does:
Other than some additional comments and clean up, that is the summary of this PR.