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
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Issue where every `NetworkObject` that started before a session logged a "Did not register because there is no session in progress!" warning when `NetworkManager.LogLevel` was `Developer`. Only `GhostObject` instances log it now. (#4245)
- Ensured a useful and human readable error is logged when a NetworkVariable is trying to serialize a private or protected type. (#4239)
- Issue where `NetworkAnimator` logged an error for a trigger-conditioned transition into an Exit node, and a client that joined during that transition, or during one into a sub-state machine, did not synchronize with it. Transitions that pass through Exit, Entry or sub-state machine nodes now resolve to the state they reach, and a late-joining client cross-fades from the matching originating state when several states transition into the same destination. (#4238)
- Ensured all callbacks are wrapped with exception handling to avoid silent errors. (#4161)
Expand Down
15 changes: 8 additions & 7 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4208,6 +4208,8 @@
[HideInInspector]
internal NetworkObjectBridge NetworkObjectBridge;

internal const string NotRegisteredWithoutSessionWarning = "Did not register because there is no session in progress!";

private void InitGhost()
{
// Note: If hybrid prefabs are created prior to any NetworkManager instances,
Expand All @@ -4220,22 +4222,21 @@
return;
}

if (!HasGhost || !NetworkObjectBridge || GhostObject.IsPrefab())
{
// Nothing to register
return;
}

if (!NetworkManager.IsListening)
{
if (NetworkManager.LogLevel == LogLevel.Developer)
{
Debug.LogWarning($"[{nameof(NetworkObject)}] Did not register because there is no session in progress!");
Debug.LogWarning($"[{nameof(NetworkObject)}][{name}] {NotRegisteredWithoutSessionWarning}");

Check warning on line 4235 in com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs#L4235

Added line #L4235 was not covered by tests
}
return;
}

if (!HasGhost || !NetworkObjectBridge || GhostObject.IsPrefab())
{
// Nothing to register
return;
}

// All instances with Ghosts are automatically registered
if (NetworkManager.LogLevel == LogLevel.Developer)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if UNIFIED_NETCODE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Unity.Netcode.TestHelpers.Runtime;
Expand All @@ -17,6 +18,7 @@ namespace Unity.Netcode.RuntimeTests
/// - A hybrid prefab instance spawned during a session that started without hybrid prefabs. It is not spawned.<br />
/// - A hybrid player prefab counts toward hybrid mode before the session starts, so distributed authority rejects it.<br />
/// - A NetworkPrefab override with a hybrid source or target prefab, in any topology. The entry is ignored.<br />
/// - A hybrid instance created before the session starts. With developer logging it warns, and a non-hybrid instance does not.<br />
/// </summary>
/// <remarks>
/// The fixture's managers are not started. Each test creates its own <see cref="NetworkManager"/> that registers only the prefabs it needs.
Expand Down Expand Up @@ -215,6 +217,58 @@ public void PrefabListAddDuringSessionRegistersOnce()
UnityEngine.Object.Destroy(prefabList);
}

/// <summary>
/// Validates that only a hybrid instance created before the session starts warns that it was not registered.
/// </summary>
[UnityTest]
public IEnumerator PreSessionInstanceWarnsOnlyWhenHybrid()
{
var networkManager = CreateNetworkManager(NetworkTopologyTypes.ClientServer);
var cases = new (GameObject Prefab, LogLevel LogLevel, bool ExpectWarning)[]
{
(m_Prefab, LogLevel.Developer, false),
(m_HybridPrefab, LogLevel.Normal, false),
(m_HybridPrefab, LogLevel.Developer, true),
};

var warnings = new List<string>();
Application.LogCallback onLogMessage = (message, stackTrace, logType) =>
{
if (logType == LogType.Warning && message.Contains(NetworkObject.NotRegisteredWithoutSessionWarning))
{
warnings.Add(message);
}
};
Application.logMessageReceived += onLogMessage;
try
{
foreach (var testCase in cases)
{
warnings.Clear();
networkManager.LogLevel = testCase.LogLevel;
var instance = UnityEngine.Object.Instantiate(testCase.Prefab).GetComponent<NetworkObject>();
instance.NetworkManagerOwner = networkManager;
var instanceName = instance.name;
var caseName = $"{instanceName}][{testCase.LogLevel}";

// Start, where the instance is registered, runs on the next frame.
yield return null;
UnityEngine.Object.Destroy(instance.gameObject);
yield return null;

Assert.AreEqual(testCase.ExpectWarning ? 1 : 0, warnings.Count, $"[{caseName}] Unexpected number of not registered warnings!");
if (testCase.ExpectWarning)
{
StringAssert.Contains(instanceName, warnings[0], $"[{caseName}] The warning does not name the instance!");
}
}
}
finally
{
Application.logMessageReceived -= onLogMessage;
}
}

/// <summary>
/// Validates that an override with a hybrid source or target is ignored, with an error naming it.
/// </summary>
Expand Down
Loading