Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
124c2b1
fix
NoelStephensUnity Jan 15, 2023
b5f8459
update
NoelStephensUnity Jan 16, 2023
673d753
fix
NoelStephensUnity Jan 16, 2023
5532515
test
NoelStephensUnity Jan 16, 2023
27c601a
Update com.unity.netcode.gameobjects/CHANGELOG.md
NoelStephensUnity Jan 19, 2023
22c5d6d
update
NoelStephensUnity Jan 19, 2023
5fc02fa
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Jan 19, 2023
f5e112c
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Feb 8, 2023
f20cf08
update
NoelStephensUnity Feb 10, 2023
5c1d1a2
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Feb 10, 2023
fbed872
update
NoelStephensUnity Feb 10, 2023
f709d76
update
NoelStephensUnity Feb 10, 2023
dcf19f5
style
NoelStephensUnity Feb 10, 2023
14287f8
revert
NoelStephensUnity Feb 10, 2023
ce39f8d
update
NoelStephensUnity Feb 10, 2023
cb391eb
update
NoelStephensUnity Feb 11, 2023
364b094
test
NoelStephensUnity Feb 11, 2023
189214d
update
NoelStephensUnity Feb 11, 2023
d793587
update
NoelStephensUnity Feb 11, 2023
79fbe81
update
NoelStephensUnity Feb 11, 2023
1ff5380
test and fix
NoelStephensUnity Feb 12, 2023
ca15b80
update and test
NoelStephensUnity Feb 12, 2023
9973752
style
NoelStephensUnity Feb 12, 2023
31d23e5
fix
NoelStephensUnity Feb 13, 2023
6f5612b
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Feb 16, 2023
db0a075
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Feb 24, 2023
8d372e6
changelog fix
NoelStephensUnity Feb 24, 2023
c39e0f7
update
NoelStephensUnity Feb 28, 2023
18a3f38
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Mar 2, 2023
dcd5053
Merge branch 'develop' into fix/clientsynchronizationmode-failure-act…
NoelStephensUnity Mar 2, 2023
3aee658
update and fix
NoelStephensUnity Mar 2, 2023
0ee065b
test
NoelStephensUnity Mar 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
- Network prefabs are now stored in a ScriptableObject that can be shared between NetworkManagers, and have been exposed for public access. By default, a Default Prefabs List is created that contains all NetworkObject prefabs in the project, and new NetworkManagers will default to using that unless that option is turned off in the Netcode for GameObjects settings. Existing NetworkManagers will maintain their existing lists, which can be migrated to the new format via a button in their inspector. (#2322)

### Fixed
- Fixed issue when the ClientSynchronizationMode is additive and the server changes the currently active scene prior to a client connecting then upon a client connecting and being synchronized the NetworkSceneManager would clear its internal ScenePlacedObjects list that could already be populated. (#2383)
Comment thread
NoelStephensUnity marked this conversation as resolved.
Outdated
- Fixed a UTP test that was failing when you install Unity Transport package 2.0.0 or newer. (#2347)
- Fixed issue where `NetcodeSettingsProvider` would throw an exception in Unity 2020.3.x versions. (#2345)
- Fixed server side issue where, depending upon component ordering, some NetworkBehaviour components might not have their OnNetworkDespawn method invoked if the client side disconnected. (#2323)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,23 @@ private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
}

~NetworkPrefabs()
{
Shutdown();
}

/// <summary>
/// Deregister from add and remove events
/// Clear the list
/// </summary>
internal void Shutdown()
{
foreach (var list in NetworkPrefabsLists)
{
list.OnAdd -= AddTriggeredByNetworkPrefabList;
list.OnRemove -= RemoveTriggeredByNetworkPrefabList;
}

NetworkPrefabsLists.Clear();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,8 @@ internal void ShutdownInternal()
m_StopProcessingMessages = false;

ClearClients();
// This cleans up the internal prefabs list
NetworkConfig?.Prefabs.Shutdown();
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


namespace Unity.Netcode
{
/// <summary>
/// The default SceneManagerHandler that interfaces between the SceneManager and NetworkSceneManager
/// </summary>
internal class DefaultSceneManagerHandler : ISceneManagerHandler
{
private Scene m_InvalidScene = new Scene();

internal struct SceneEntry
{
public bool IsAssigned;
public Scene Scene;
}

internal Dictionary<string, Dictionary<int, SceneEntry>> SceneNameToSceneHandles = new Dictionary<string, Dictionary<int, SceneEntry>>();

public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
{
var operation = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}

public AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress)
{
var operation = SceneManager.UnloadSceneAsync(scene);
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}

/// <summary>
/// Resets scene tracking
/// </summary>
public void ClearSceneTracking(NetworkManager networkManager)
{
SceneNameToSceneHandles.Clear();
}

/// <summary>
/// Stops tracking a specific scene
/// </summary>
public void StopTrackingScene(int handle, string name, NetworkManager networkManager)
{
if (SceneNameToSceneHandles.ContainsKey(name))
{
if (SceneNameToSceneHandles[name].ContainsKey(handle))
{
SceneNameToSceneHandles[name].Remove(handle);
if (SceneNameToSceneHandles[name].Count == 0)
{
SceneNameToSceneHandles.Remove(name);
}
}
}
}

/// <summary>
/// Starts tracking a specific scene
/// </summary>
public void StartTrackingScene(Scene scene, bool assigned, NetworkManager networkManager)
{
if (!SceneNameToSceneHandles.ContainsKey(scene.name))
{
SceneNameToSceneHandles.Add(scene.name, new Dictionary<int, SceneEntry>());
}

if (!SceneNameToSceneHandles[scene.name].ContainsKey(scene.handle))
{
var sceneEntry = new SceneEntry()
{
IsAssigned = true,
Scene = scene
};
SceneNameToSceneHandles[scene.name].Add(scene.handle, sceneEntry);
}
else
{
throw new Exception($"[Duplicate Handle] Scene {scene.name} already has scene handle {scene.handle} registered!");
}
}

/// <summary>
/// Determines if there is an existing scene loaded that matches the scene name but has not been assigned
/// </summary>
public bool DoesSceneHaveUnassignedEntry(string sceneName, NetworkManager networkManager)
{
if (SceneNameToSceneHandles.ContainsKey(sceneName))
{
foreach (var sceneHandleEntry in SceneNameToSceneHandles[sceneName])
{
if (!sceneHandleEntry.Value.IsAssigned)
{
return true;
}
}
}
return false;
}

/// <summary>
/// This will find any scene entry that hasn't been used/assigned, set the entry to assigned, and
/// return the associated scene. If none are found it returns an invalid scene.
/// </summary>
public Scene GetSceneFromLoadedScenes(string sceneName, NetworkManager networkManager)
{
if (SceneNameToSceneHandles.ContainsKey(sceneName))
{
foreach (var sceneHandleEntry in SceneNameToSceneHandles[sceneName])
{
if (!sceneHandleEntry.Value.IsAssigned)
{
var sceneEntry = sceneHandleEntry.Value;
sceneEntry.IsAssigned = true;
SceneNameToSceneHandles[sceneName][sceneHandleEntry.Key] = sceneEntry;
return sceneEntry.Scene;
}
}
}
// If we found nothing return an invalid scene
return m_InvalidScene;
}

/// <summary>
/// Only invoked is client synchronization is additive, this will generate the scene tracking table
/// in order to re-use the same scenes the server is synchronizing instead of having to unload the
/// scenes and reload them when synchronizing (i.e. client disconnects due to external reason, the
/// same application instance is still running, the same scenes are still loaded on the client, and
/// upon reconnecting the client doesn't have to unload the scenes and then reload them)
/// </summary>
public void PopulateLoadedScenes(ref Dictionary<int, Scene> scenesLoaded, NetworkManager networkManager)
{
SceneNameToSceneHandles.Clear();
var sceneCount = SceneManager.sceneCount;
for (int i = 0; i < sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (!SceneNameToSceneHandles.ContainsKey(scene.name))
{
SceneNameToSceneHandles.Add(scene.name, new Dictionary<int, SceneEntry>());
}

if (!SceneNameToSceneHandles[scene.name].ContainsKey(scene.handle))
{
var sceneEntry = new SceneEntry()
{
IsAssigned = false,
Scene = scene
};
SceneNameToSceneHandles[scene.name].Add(scene.handle, sceneEntry);
if (!scenesLoaded.ContainsKey(scene.handle))
{
scenesLoaded.Add(scene.handle, scene);
}
}
else
{
throw new Exception($"[Duplicate Handle] Scene {scene.name} already has scene handle {scene.handle} registered!");
}
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

Expand All @@ -12,5 +13,16 @@ internal interface ISceneManagerHandler
AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress);

AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress);

void PopulateLoadedScenes(ref Dictionary<int, Scene> scenesLoaded, NetworkManager networkManager = null);
Scene GetSceneFromLoadedScenes(string sceneName, NetworkManager networkManager = null);

bool DoesSceneHaveUnassignedEntry(string sceneName, NetworkManager networkManager = null);

void StopTrackingScene(int handle, string name, NetworkManager networkManager = null);

void StartTrackingScene(Scene scene, bool assigned, NetworkManager networkManager = null);

void ClearSceneTracking(NetworkManager networkManager = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -333,26 +333,6 @@ public class NetworkSceneManager : IDisposable
/// </summary>
internal ISceneManagerHandler SceneManagerHandler = new DefaultSceneManagerHandler();

/// <summary>
/// The default SceneManagerHandler that interfaces between the SceneManager and NetworkSceneManager
/// </summary>
private class DefaultSceneManagerHandler : ISceneManagerHandler
{
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
{
var operation = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}

public AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress)
{
var operation = SceneManager.UnloadSceneAsync(scene);
sceneEventProgress.SetAsyncOperation(operation);
return operation;
}
}

internal readonly Dictionary<Guid, SceneEventProgress> SceneEventProgressTracking = new Dictionary<Guid, SceneEventProgress>();

/// <summary>
Expand Down Expand Up @@ -675,11 +655,11 @@ internal Scene GetAndAddNewlyLoadedSceneByName(string sceneName)
if (!ScenesLoaded.ContainsKey(sceneLoaded.handle))
{
ScenesLoaded.Add(sceneLoaded.handle, sceneLoaded);
SceneManagerHandler.StartTrackingScene(sceneLoaded, true, m_NetworkManager);
return sceneLoaded;
}
}
}

throw new Exception($"Failed to find any loaded scene named {sceneName}!");
}
}
Expand Down Expand Up @@ -1013,7 +993,7 @@ private void OnClientUnloadScene(uint sceneEventId)
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(ScenesLoaded[sceneHandle], sceneEventProgress);

ScenesLoaded.Remove(sceneHandle);

SceneManagerHandler.StopTrackingScene(sceneHandle, sceneName, m_NetworkManager);
// Remove our server to scene handle lookup
ServerSceneHandleToClientSceneHandle.Remove(sceneEventData.SceneHandle);

Expand Down Expand Up @@ -1112,6 +1092,7 @@ internal void UnloadAdditivelyLoadedScenes(uint sceneEventId)
}
// clear out our scenes loaded list
ScenesLoaded.Clear();
SceneManagerHandler.ClearSceneTracking(m_NetworkManager);
}

/// <summary>
Expand Down Expand Up @@ -1530,7 +1511,7 @@ internal void SynchronizeNetworkObjects(ulong clientId)
m_NetworkManager.SpawnManager.UpdateObservedNetworkObjects(clientId);

var sceneEventData = BeginSceneEvent();

sceneEventData.ClientSynchronizationMode = ClientSynchronizationMode;
sceneEventData.InitializeForSynch();
sceneEventData.TargetClientId = clientId;
sceneEventData.LoadSceneMode = ClientSynchronizationMode;
Expand Down Expand Up @@ -1620,9 +1601,6 @@ private void OnClientBeginSync(uint sceneEventId)
});

OnSynchronize?.Invoke(m_NetworkManager.LocalClientId);

// Clear the in-scene placed NetworkObjects when we load the first scene in our synchronization process
ScenePlacedObjects.Clear();
}

// Always check to see if the scene needs to be validated
Expand All @@ -1636,11 +1614,12 @@ private void OnClientBeginSync(uint sceneEventId)
return;
}

var shouldPassThrough = false;
var shouldPassThrough = ClientSynchronizationMode == LoadSceneMode.Single ? false : SceneManagerHandler.DoesSceneHaveUnassignedEntry(sceneName, m_NetworkManager);
var sceneLoad = (AsyncOperation)null;

// Check to see if the client already has loaded the scene to be loaded
if (sceneName == activeScene.name)
// Check to see if the client already has loaded the scene to be loaded and the client synchronization
// mode is set to additive
if (sceneName == activeScene.name && ClientSynchronizationMode == LoadSceneMode.Additive)
{
// If the client is already in the same scene, then pass through and
// don't try to reload it.
Expand Down Expand Up @@ -1683,7 +1662,11 @@ private void ClientLoadedSynchronization(uint sceneEventId)
{
var sceneEventData = SceneEventDataStore[sceneEventId];
var sceneName = SceneNameFromHash(sceneEventData.ClientSceneHash);
var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
var nextScene = SceneManagerHandler.GetSceneFromLoadedScenes(sceneName, m_NetworkManager);
if (!nextScene.IsValid())
{
nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
}

if (!nextScene.isLoaded || !nextScene.IsValid())
{
Expand Down Expand Up @@ -1937,6 +1920,8 @@ private void HandleServerSceneEvent(uint sceneEventId, ulong clientId)
}
}



/// <summary>
/// Both Client and Server: Incoming scene event entry point
/// </summary>
Expand All @@ -1955,6 +1940,23 @@ internal void HandleSceneEvent(ulong clientId, FastBufferReader reader)

if (sceneEventData.IsSceneEventClientSide())
{
// If the client is being synchronized for the first time do some initialization
if (sceneEventData.SceneEventType == SceneEventType.Synchronize)
{
ScenePlacedObjects.Clear();
// Set the server's configured client synchronization mode on the client side
ClientSynchronizationMode = sceneEventData.ClientSynchronizationMode;

// Only if ClientSynchronizationMode is Additive and the client receives a synchronize scene event
if (ClientSynchronizationMode == LoadSceneMode.Additive)
{
// Check for scenes already loaded and create a table of scenes already loaded (SceneEntries) that will be
// used if the server is synchronizing the same scenes (i.e. if a matching scene is already loaded on the
// client side, then that scene will be used as opposed to loading another scene). This allows for clients
// to reconnect to a network session without having to unload all of the scenes and reload all of the scenes.
SceneManagerHandler.PopulateLoadedScenes(ref ScenesLoaded, m_NetworkManager);
}
}
HandleClientSceneEvent(sceneEventData.SceneEventId);
}
else
Expand Down
Loading