Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Changed




### Deprecated


Expand All @@ -22,6 +24,10 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Issue with not being able to spawn initially disabled in-scene placed objects. (#4093)
- Issue with pre-instantiated network prefab instances being marked as in-scene placed. Now pre-instantiated network prefabs are dynamically spawned. (#4093)
- Issue where a user could spawn runtime created `NetworkObject` that has a GlobalObjectIdHash of zero. These are not valid instances and will no longer be allowed to spawn. (#4093)


### Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,13 @@ public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour
> [!NOTE]
> You only need to enable the NetworkObject on the server-side to be able to respawn it. Netcode for GameObjects only enables a disabled in-scene placed NetworkObject on the client-side if the server-side spawns it. This **does not** apply to dynamically spawned `NetworkObjects`. Refer to [the object pooling page](../../advanced-topics/object-pooling.md) for an example of recycling dynamically spawned NetworkObjects.

### Pre-disabled in-scene placed NetworkObjects

If you want to always have an in-scene placed NetworkObject start off as disabled and then spawn it at a later time, you only need to set its GameObject to inactive in the editor while the respective scene is open. Once you have started a networked session, you can re-enable it and then spawn it any time.

### Setting an in-scene placed NetworkObject to a despawned state when instantiating

Since in-scene placed NetworkObjects are automatically spawned when their respective scene has finished loading during a network session, you might run into the scenario where you want it to start in a despawned state until a certain condition has been met. To do this, you need to add some additional code in the `OnNetworkSpawn` part of your NetworkBehaviour component:
If you want to programmatically disable an in-scene placed NetworkObject you can add some additional code in the `OnNetworkSpawn` part of a NetworkBehaviour component:

```csharp
using UnityEngine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@ When spawning a NetworkObject, the `NetworkObject.GlobalObjectIdHash` value init

You can use [NetworkBehaviours](networkbehaviour.md) to add your own custom Netcode logic to the associated NetworkObject.

### What is a valid NetworkObject?

There are two categories of NetworkObjects:
* Dynamically intantiated
* [In-scene placed](../../basics/scenemanagement/inscene-placed-networkobjects.md)

The following provides the validity requirements for both types.

#### Dynamically instantiated network prefabs.

Requirements

* It must be a valid [network prefab](./networkobject.md#network-prefabs) created within the editor.
* It must be registered within a network prefab list that is assigned to your NetworkManager.

#### In-scene placed

Requirements

* It can be a valid network prefab instance within a scene.
* It can be a GameObject with a `NetworkObject` component created within the scene while in the editor.

### What is an invalid NetworkObject?

Any GameObjects that have `NetworkObject` components added to them during runtime is **not supported** and will result in the NetworkObject's `GlobalObjectIdHash` being zero which would eventually result in synchronization issues. In the event you make this mistake, a warning message will be logged and the NetworkObject will not get spawned.

### Component order

The order of components on a networked GameObject matters. When adding netcode components to a GameObject, ensure that the NetworkObject component is ordered before any NetworkBehaviour components.
Expand Down
14 changes: 14 additions & 0 deletions com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
log.AddInfo(scene.name, scene.handle);
foreach (var networkObject in FindObjects.FromSceneByType<NetworkObject>(scene, true))
{
// Trap for users just creating things during runtime where this will be zero.
if (networkObject.GlobalObjectIdHash == 0)
{
log.Warning(new Context(LogLevel.Developer, $"{nameof(NetworkObject)}'s GlobalObjectIdHash value is zero! Runtime creating of {nameof(NetworkObject)}s is not supported. Skipping processing.").AddNetworkObject(networkObject));
continue;

Check warning on line 31 in com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs#L28-L31

Added lines #L28 - L31 were not covered by tests
}
if (networkObject.SceneOrigin.IsValid() && networkObject.SceneOrigin.handle != scene.handle)
{
log.Warning(new Context(LogLevel.Developer, $"{nameof(NetworkObject)}'s SceneOrigin doesn't match current scene being processed! Skipping processing.").AddInfo("SceneOrigin", networkObject.SceneOriginHandle).AddNetworkObject(networkObject));
Expand All @@ -36,7 +42,15 @@
continue;
}

// If already marked, the do nothing.
if (networkObject.InScenePlaced)
{
continue;

Check warning on line 48 in com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs#L46-L48

Added lines #L46 - L48 were not covered by tests
}

networkObject.InScenePlaced = true;
// Will not be true when making a build and the values are serialized.
networkObject.InScenePlacedPostProcessorMarkedDuringRuntime = Application.isPlaying;

Check warning on line 53 in com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs#L53

Added line #L53 was not covered by tests
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ protected virtual void Awake()
if (!m_Animator)
{
#if !UNITY_EDITOR
Debug.LogError($"{nameof(NetworkAnimator)} {name} does not have an {nameof(UnityEngine.Animator)} assigned to it. The {nameof(NetworkAnimator)} will not initialize properly.");
Debug.LogWarning($"{nameof(NetworkAnimator)} {name} does not have an {nameof(UnityEngine.Animator)} assigned to it. The {nameof(NetworkAnimator)} will not initialize properly.");
#endif
return;
}
Expand Down
124 changes: 100 additions & 24 deletions com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,110 @@
[NonSerialized]
private List<NetworkPrefab> m_Prefabs = new List<NetworkPrefab>();

/// <summary>
/// Returns the last registered prefab.
/// </summary>
internal NetworkPrefab GetLastRegisteredPrefab()
{
if (m_Prefabs.Count == 0)
{
return null;

Check warning on line 57 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs#L56-L57

Added lines #L56 - L57 were not covered by tests
}
return m_Prefabs[m_Prefabs.Count - 1];
}

/// <summary>
/// Applies a network prefab at a specific index
/// </summary>
/// <param name="index">index to apply</param>
/// <param name="networkPrefab">network prefab to be applied</param>
/// <returns></returns>
internal bool AssignPrefabAtIndex(int index, NetworkPrefab networkPrefab)
{
if (index >= m_Prefabs.Count)
{
NetworkManager.Singleton.Log.Error(new Logging.Context(LogLevel.Normal, $"[{nameof(NetworkPrefabs)}][{nameof(AssignPrefabAtIndex)}] Cannot apply prefab to index {index} when the {nameof(m_Prefabs)} count is only {m_Prefabs.Count}!"));
return false;

Check warning on line 73 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs#L71-L73

Added lines #L71 - L73 were not covered by tests
}
m_Prefabs[index] = networkPrefab;
return true;
}

[NonSerialized]
private Dictionary<uint, NetworkPrefab> m_PrefabHashIds = new Dictionary<uint, NetworkPrefab>();

[NonSerialized]
private List<NetworkPrefab> m_RuntimeAddedPrefabs = new List<NetworkPrefab>();

private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
private bool InternalAddPrefab(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
{
// Don't add this to m_RuntimeAddedPrefabs
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
m_Prefabs.Add(networkPrefab);

// We are not getting all potential overrides but just determining if the prefab has been registered.
if (!m_PrefabHashIds.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash))
{
m_PrefabHashIds.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab);
}
if (!m_PrefabHashIds.ContainsKey(networkPrefab.TargetPrefabGlobalObjectIdHash))
{
m_PrefabHashIds.Add(networkPrefab.TargetPrefabGlobalObjectIdHash, networkPrefab);
}
return true;
}
return false;
}

private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
private void InternalRemovePrefab(NetworkPrefab networkPrefab)
{
m_Prefabs.Remove(networkPrefab);
m_PrefabHashIds.Remove(networkPrefab.SourcePrefabGlobalObjectIdHash);
}

internal bool IsBasedOnRegisteredPrefab(NetworkObject networkObject)
{


return m_PrefabHashIds.ContainsKey(networkObject.GlobalObjectIdHash);
}

internal bool IsActualPrefabAsset(NetworkObject networkObject)
{
var isActualPrefabAsset = false;
Comment thread
NoelStephensUnity marked this conversation as resolved.
if (m_PrefabHashIds.TryGetValue(networkObject.GlobalObjectIdHash, out NetworkPrefab networkPrefab))
{
switch (networkPrefab.Override)
{
case NetworkPrefabOverride.Prefab:
case NetworkPrefabOverride.None:
{
isActualPrefabAsset = networkPrefab.Prefab != null && networkObject.gameObject == networkPrefab.Prefab;
break;
}
case NetworkPrefabOverride.Hash:
{
isActualPrefabAsset = networkPrefab.SourceHashToOverride == networkObject.GlobalObjectIdHash;
break;
}
}
}
return isActualPrefabAsset;
}

private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
// Don't add this to m_RuntimeAddedPrefabs
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
InternalAddPrefab(networkPrefab);
// Log warning if this returns false?
}

private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
InternalRemovePrefab(networkPrefab);

Check warning on line 153 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs#L152-L153

Added lines #L152 - L153 were not covered by tests
}

/// <summary>
Expand Down Expand Up @@ -93,6 +181,7 @@
/// <param name="warnInvalid">When true, logs warnings about invalid prefabs that are removed during initialization</param>
public void Initialize(bool warnInvalid = true)
{
m_PrefabHashIds.Clear();
m_Prefabs.Clear();
NetworkPrefabsLists.RemoveAll(x => x == null);
foreach (var list in NetworkPrefabsLists)
Expand All @@ -113,7 +202,7 @@
prefabs.AddRange(list.PrefabList);
}
}

m_PrefabHashIds = new Dictionary<uint, NetworkPrefab>();
m_Prefabs = new List<NetworkPrefab>();

List<NetworkPrefab> removeList = null;
Expand All @@ -124,23 +213,15 @@

foreach (var networkPrefab in prefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
if (!InternalAddPrefab(networkPrefab))
{
removeList?.Add(networkPrefab);
}
}

foreach (var networkPrefab in m_RuntimeAddedPrefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
if (!InternalAddPrefab(networkPrefab))
{
removeList?.Add(networkPrefab);
}
Expand Down Expand Up @@ -171,14 +252,12 @@
/// </remarks>
public bool Add(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
var added = InternalAddPrefab(networkPrefab);
if (added)
{
m_Prefabs.Add(networkPrefab);
m_RuntimeAddedPrefabs.Add(networkPrefab);
return true;
}

return false;
return added;
}

/// <summary>
Expand All @@ -197,8 +276,7 @@
{
throw new ArgumentNullException(nameof(prefab));
}

m_Prefabs.Remove(prefab);
InternalRemovePrefab(prefab);
m_RuntimeAddedPrefabs.Remove(prefab);
OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash);
NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash);
Expand Down Expand Up @@ -294,14 +372,12 @@

uint source = networkPrefab.SourcePrefabGlobalObjectIdHash;
uint target = networkPrefab.TargetPrefabGlobalObjectIdHash;

// Make sure the prefab isn't already registered.
if (NetworkPrefabOverrideLinks.ContainsKey(source))
{
var networkObject = networkPrefab.Prefab.GetComponent<NetworkObject>();

var nameOrHashOverride = networkPrefab.Override == NetworkPrefabOverride.Hash ? $"Hash: {networkPrefab.SourcePrefabGlobalObjectIdHash}" : networkPrefab.Prefab?.name;

Check warning on line 378 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs#L378

Added line #L378 was not covered by tests
Comment thread
NoelStephensUnity marked this conversation as resolved.
// This should never happen, but in the case it somehow does log an error and remove the duplicate entry
Debug.LogError($"{nameof(NetworkPrefab)} ({networkObject.name}) has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} source entry value of: {source}!");
Debug.LogError($"{nameof(NetworkPrefab)} ({nameOrHashOverride}) has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} source entry value of: {source}!");

Check warning on line 380 in com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs#L380

Added line #L380 was not covered by tests
return false;
}

Expand Down
Loading