Additive sceneloading sceneid fix (#175)

* Fix additive scene loading causing duplicate sceneIds by using an offset per scene.

* TEST: moved SpawnableObjects to NetworkIdentity. OnDisable adds it to the dict. SpawnObject removes it.

* Log if we don't find the spawn scene object

* Fix compilation issues

* Make PrepareToSpawnSceneObjects public
This commit is contained in:
Paul Pacheco 2019-01-14 15:00:13 -06:00 committed by vis2k
parent 1785039601
commit 3c0ff33c94
3 changed files with 26 additions and 4 deletions

View File

@ -4,6 +4,7 @@
using UnityEditor; using UnityEditor;
using UnityEditor.Callbacks; using UnityEditor.Callbacks;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace Mirror namespace Mirror
{ {
@ -133,6 +134,19 @@ public static void OnPostProcessScene()
List<NetworkIdentity> identities = FindObjectsOfType<NetworkIdentity>().ToList(); List<NetworkIdentity> identities = FindObjectsOfType<NetworkIdentity>().ToList();
identities.Sort(CompareNetworkIdentitySiblingPaths); identities.Sort(CompareNetworkIdentitySiblingPaths);
// sceneId assignments need to work with additive scene loading, so
// it can't always start at 1,2,3,4,..., otherwise there will be
// sceneId duplicates.
// -> we need an offset to start at 1000+1,+2,+3, etc.
// -> the most robust way is to split uint value range by sceneCount
uint offsetPerScene = uint.MaxValue / (uint)SceneManager.sceneCountInBuildSettings;
// make sure that there aren't more sceneIds than offsetPerScene
if (identities.Count >= offsetPerScene)
{
Debug.LogWarning(">" + offsetPerScene + " NetworkIdentities in scene. Additive scene loading will cause duplicate ids.");
}
uint nextSceneId = 1; uint nextSceneId = 1;
foreach (NetworkIdentity identity in identities) foreach (NetworkIdentity identity in identities)
{ {
@ -145,10 +159,15 @@ public static void OnPostProcessScene()
if (identity.isClient || identity.isServer) if (identity.isClient || identity.isServer)
continue; continue;
identity.gameObject.SetActive(false); uint offset = (uint)identity.gameObject.scene.buildIndex * offsetPerScene;
identity.ForceSceneId(nextSceneId++); identity.ForceSceneId(offset + nextSceneId++);
if (LogFilter.Debug) { Debug.Log("PostProcess sceneid assigned: name=" + identity.name + " scene=" + identity.gameObject.scene.name + " sceneid=" + identity.sceneId); } if (LogFilter.Debug) { Debug.Log("PostProcess sceneid assigned: name=" + identity.name + " scene=" + identity.gameObject.scene.name + " sceneid=" + identity.sceneId); }
// disable it AFTER assigning the sceneId.
// -> this way NetworkIdentity.OnDisable adds itself to the
// spawnableObjects dictionary (only if sceneId != 0)
identity.gameObject.SetActive(false);
// safety check for prefabs with more than one NetworkIdentity // safety check for prefabs with more than one NetworkIdentity
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
GameObject prefabGO = PrefabUtility.GetCorrespondingObjectFromSource(identity.gameObject); GameObject prefabGO = PrefabUtility.GetCorrespondingObjectFromSource(identity.gameObject);

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
@ -180,7 +180,9 @@ internal static bool ConsiderForSpawning(NetworkIdentity identity)
identity.sceneId != 0; identity.sceneId != 0;
} }
internal static void PrepareToSpawnSceneObjects() // this needs to be public. If users load/unload a scene in the client after connection
// they should call this to register the spawnable objects
public static void PrepareToSpawnSceneObjects()
{ {
// add all unspawned NetworkIdentities to spawnable objects // add all unspawned NetworkIdentities to spawnable objects
spawnableObjects = Resources.FindObjectsOfTypeAll<NetworkIdentity>() spawnableObjects = Resources.FindObjectsOfTypeAll<NetworkIdentity>()

View File

@ -97,6 +97,7 @@ public Guid assetId
return string.IsNullOrEmpty(m_AssetId) ? Guid.Empty : new Guid(m_AssetId); return string.IsNullOrEmpty(m_AssetId) ? Guid.Empty : new Guid(m_AssetId);
} }
} }
internal void SetDynamicAssetId(Guid newAssetId) internal void SetDynamicAssetId(Guid newAssetId)
{ {
string newAssetIdString = newAssetId.ToString("N"); string newAssetIdString = newAssetId.ToString("N");