Skip to content
Merged
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
13 changes: 8 additions & 5 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ Additional documentation and release notes are available at [Multiplayer Documen

## Changed

## [1.4.0]

## [1.4.0] - 2023-04-10

### Added

- Added a way to access the GlobalObjectIdHash via PrefabIdHash for use in the Connection Approval Callback. (#2437)
- Added `OnServerStarted` and `OnServerStopped` events that will trigger only on the server (or host player) to notify that the server just started or is no longer active (#2420)
- Added `OnClientStarted` and `OnClientStopped` events that will trigger only on the client (or host player) to notify that the client just started or is no longer active (#2420)
- Added `NetworkTransform.UseHalfFloatPrecision` property that, when enabled, will use half float values for position, rotation, and scale. This yields a 50% bandwidth savings a the cost of precision. (#2388)
- Added `NetworkTransform.UseQuaternionSynchronization` property that, when enabled, will synchronize the entire quaternion. (#2388)
- Added `NetworkTransform.UseQuaternionCompression` property that, when enabled, will use a smallest three implementation reducing a full quaternion synchronization update to the size of an unsigned integer. (#2388)
Expand All @@ -41,22 +44,23 @@ Additional documentation and release notes are available at [Multiplayer Documen
- Added `NetworkSceneManager.ActiveSceneSynchronizationEnabled` property, disabled by default, that enables client synchronization of server-side active scene changes. (#2383)
- Added `NetworkObject.ActiveSceneSynchronization`, disabled by default, that will automatically migrate a `NetworkObject` to a newly assigned active scene. (#2383)
- Added `NetworkObject.SceneMigrationSynchronization`, enabled by default, that will synchronize client(s) when a `NetworkObject` is migrated into a new scene on the server side via `SceneManager.MoveGameObjectToScene`. (#2383)
- Added `OnServerStarted` and `OnServerStopped` events that will trigger only on the server (or host player) to notify that the server just started or is no longer active (#2420)
- Added `OnClientStarted` and `OnClientStopped` events that will trigger only on the client (or host player) to notify that the client just started or is no longer active (#2420)

### Changed

- Made sure the `CheckObjectVisibility` delegate is checked and applied, upon `NetworkShow` attempt. Found while supporting (#2454), although this is not a fix for this (already fixed) issue. (#2463)
- Changed `NetworkTransform` authority handles delta checks on each new network tick and no longer consumes processing cycles checking for deltas for all frames in-between ticks. (#2388)
- Changed the `NetworkTransformState` structure is now public and now has public methods that provide access to key properties of the `NetworkTransformState` structure. (#2388)
- Changed `NetworkTransform` interpolation adjusts its interpolation "ticks ago" to be 2 ticks latent if it is owner authoritative and the instance is not the server or 1 tick latent if the instance is the server and/or is server authoritative. (#2388)
- Updated `NetworkSceneManager` to migrate dynamically spawned `NetworkObject`s with `DestroyWithScene` set to false into the active scene if their current scene is unloaded. (#2383)
- Updated the server to synchronize its local `NetworkSceneManager.ClientSynchronizationMode` during the initial client synchronization. (#2383)
- Made sure the `CheckObjectVisibility` delegate is checked and applied, upon `NetworkShow` attempt. Found while supporting (#2454), although this is not a fix for this (already fixed) issue. (#2463)

### Fixed

- Fixed issue where during client synchronization the synchronizing client could receive a ObjectSceneChanged message before the client-side NetworkObject instance had been instantiated and spawned. (#2502)
- Fixed issue where `NetworkAnimator` was building client RPC parameters to exclude the host from sending itself messages but was not including it in the ClientRpc parameters. (#2492)
- Fixed issue where `NetworkAnimator` was not properly detecting and synchronizing cross fade initiated transitions. (#2481)
- Fixed issue where `NetworkAnimator` was not properly synchronizing animation state updates. (#2481)
- Fixed float NetworkVariables not being rendered properly in the inspector of NetworkObjects. (#2441)
- Fixed an issue where Named Message Handlers could remove themselves causing an exception when the metrics tried to access the name of the message.(#2426)
- Fixed registry of public `NetworkVariable`s in derived `NetworkBehaviour`s (#2423)
- Fixed issue where runtime association of `Animator` properties to `AnimationCurve`s would cause `NetworkAnimator` to attempt to update those changes. (#2416)
Expand All @@ -65,7 +69,6 @@ Additional documentation and release notes are available at [Multiplayer Documen
- Fixed issue where `NetworkTransform` was not setting the teleport flag when the `NetworkTransform.InLocalSpace` value changed. This issue only impacted `NetworkTransform` when interpolation was enabled. (#2388)
- Fixed issue when the `NetworkSceneManager.ClientSynchronizationMode` is `LoadSceneMode.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)
- Fixed issue where a client would load duplicate scenes of already preloaded scenes during the initial client synchronization and `NetworkSceneManager.ClientSynchronizationMode` was set to `LoadSceneMode.Additive`. (#2383)
- Fixed float NetworkVariables not being rendered properly in the inspector of NetworkObjects. (#2441)

## [1.3.1] - 2023-03-27

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ internal void CheckForAnimatorChanges()
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
m_ClientSendList.Remove(NetworkManager.LocalClientId);
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
SendAnimStateClientRpc(m_AnimationMessage);
SendAnimStateClientRpc(m_AnimationMessage, m_ClientRpcParams);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2003,7 +2003,7 @@ private void ApplyTeleportingState(NetworkTransformState newState)

if (Interpolate)
{
UpdatePositionInterpolator(currentPosition, sentTime);
UpdatePositionInterpolator(currentPosition, sentTime, true);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,11 @@ private void HandleClientSceneEvent(uint sceneEventId)
ClientId = NetworkManager.LocalClientId, // Client sent this to the server
});

// Process any SceneEventType.ObjectSceneChanged messages that
// were deferred while synchronizing and migrate the associated
// NetworkObjects to their newly assigned scenes.
sceneEventData.ProcessDeferredObjectSceneChangedEvents();

// Only if PostSynchronizationSceneUnloading is set and we are running in client synchronization
// mode additive do we unload any remaining scene that was not synchronized (otherwise any loaded
// scene not synchronized by the server will remain loaded)
Expand Down Expand Up @@ -2412,6 +2417,7 @@ internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject)
}

// Don't notify if a scene event is in progress
// Note: This does not apply to SceneEventType.Synchronize since synchronization isn't a global connected client event.
foreach (var sceneEventEntry in SceneEventProgressTracking)
{
if (!sceneEventEntry.Value.HasTimedOut() && sceneEventEntry.Value.Status == SceneEventProgressStatus.Started)
Expand All @@ -2430,8 +2436,10 @@ internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject)

/// <summary>
/// Invoked by clients when processing a <see cref="SceneEventType.ObjectSceneChanged"/> event
/// or invoked by <see cref="SceneEventData.ProcessDeferredObjectSceneChangedEvents"/> when a client finishes
/// synchronization.
/// </summary>
private void MigrateNetworkObjectsIntoScenes()
internal void MigrateNetworkObjectsIntoScenes()
{
try
{
Expand Down Expand Up @@ -2476,5 +2484,13 @@ internal void CheckForAndSendNetworkObjectSceneChanged()
SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
EndSceneEvent(sceneEvent.SceneEventId);
}

// Used to handle client-side scene migration messages received while
// a client is synchronizing
internal struct DeferredObjectsMovedEvent
{
internal Dictionary<int, List<ulong>> ObjectsMigratedTable;
}
internal List<DeferredObjectsMovedEvent> DeferredObjectsMovedEvents = new List<DeferredObjectsMovedEvent>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,15 @@ internal void Deserialize(FastBufferReader reader)

if (SceneEventType == SceneEventType.ObjectSceneChanged)
{
DeserializeObjectsMovedIntoNewScene(reader);
// Defer these scene event types if a client hasn't finished synchronizing
if (!m_NetworkManager.IsConnectedClient)
{
DeferObjectsMovedIntoNewScene(reader);
}
else
{
DeserializeObjectsMovedIntoNewScene(reader);
}
return;
}

Expand Down Expand Up @@ -1036,14 +1044,94 @@ private void DeserializeObjectsMovedIntoNewScene(FastBufferReader reader)
reader.ReadValueSafe(out networkObjectId);
if (!spawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
throw new Exception($"[Object Scene Migration] Trying to synchronize NetworkObjectId ({networkObjectId}) but it no longer exists!");
NetworkLog.LogError($"[Object Scene Migration] Trying to synchronize NetworkObjectId ({networkObjectId}) but it was not spawned or no longer exists!!");
continue;
}
// Add NetworkObject scene migration to ObjectsMigratedIntoNewScene dictionary that is processed
//
sceneManager.ObjectsMigratedIntoNewScene[sceneHandle].Add(spawnManager.SpawnedObjects[networkObjectId]);
}
}
}


/// <summary>
/// While a client is synchronizing ObjectSceneChanged messages could be received.
/// This defers any ObjectSceneChanged message processing to occur after the client
/// has completed synchronization to assure the associated NetworkObjects being
/// migrated to a new scene are instantiated and spawned.
/// </summary>
private void DeferObjectsMovedIntoNewScene(FastBufferReader reader)
{
var sceneManager = m_NetworkManager.SceneManager;
var spawnManager = m_NetworkManager.SpawnManager;
var numberOfScenes = 0;
var sceneHandle = 0;
var objectCount = 0;
var networkObjectId = (ulong)0;

var deferredObjectsMovedEvent = new NetworkSceneManager.DeferredObjectsMovedEvent()
{
ObjectsMigratedTable = new Dictionary<int, List<ulong>>()
};

reader.ReadValueSafe(out numberOfScenes);
for (int i = 0; i < numberOfScenes; i++)
{
reader.ReadValueSafe(out sceneHandle);
deferredObjectsMovedEvent.ObjectsMigratedTable.Add(sceneHandle, new List<ulong>());
reader.ReadValueSafe(out objectCount);
for (int j = 0; j < objectCount; j++)
{
reader.ReadValueSafe(out networkObjectId);
deferredObjectsMovedEvent.ObjectsMigratedTable[sceneHandle].Add(networkObjectId);
}
}
sceneManager.DeferredObjectsMovedEvents.Add(deferredObjectsMovedEvent);
}

internal void ProcessDeferredObjectSceneChangedEvents()
{
var sceneManager = m_NetworkManager.SceneManager;
var spawnManager = m_NetworkManager.SpawnManager;
if (sceneManager.DeferredObjectsMovedEvents.Count == 0)
{
return;
}
foreach (var objectsMovedEvent in sceneManager.DeferredObjectsMovedEvents)
{
foreach (var keyEntry in objectsMovedEvent.ObjectsMigratedTable)
{
if (!sceneManager.ObjectsMigratedIntoNewScene.ContainsKey(keyEntry.Key))
{
sceneManager.ObjectsMigratedIntoNewScene.Add(keyEntry.Key, new List<NetworkObject>());
}
foreach (var objectId in keyEntry.Value)
{
if (!spawnManager.SpawnedObjects.ContainsKey(objectId))
{
NetworkLog.LogWarning($"[Deferred][Object Scene Migration] Trying to synchronize NetworkObjectId ({objectId}) but it was not spawned or no longer exists!");
continue;
}
var networkObject = spawnManager.SpawnedObjects[objectId];
if (!sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].Contains(networkObject))
{
sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].Add(networkObject);
}
}
}
objectsMovedEvent.ObjectsMigratedTable.Clear();
}

sceneManager.DeferredObjectsMovedEvents.Clear();

// If there are any pending objects to migrate, then migrate them
if (sceneManager.ObjectsMigratedIntoNewScene.Count > 0)
{
sceneManager.MigrateNetworkObjectsIntoScenes();
}
}

/// <summary>
/// Used to release the pooled network buffer
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -930,8 +930,9 @@ public void TeleportTest([Values] Interpolation interpolation, [Values] Precisio
var success = WaitForConditionOrTimeOutWithTimeTravel(() => m_AuthoritativeTransform.StatePushed && m_NonAuthoritativeTransform.StateUpdated);
Assert.True(success, $"[Teleport] Timed out waiting for state to be pushed ({m_AuthoritativeTransform.StatePushed}) or state to be updated ({m_NonAuthoritativeTransform.StateUpdated})!");

success = WaitForConditionOrTimeOutWithTimeTravel(() => TeleportPositionMatches(nonAuthPosition));
Assert.True(success, $"[Timed-Out][Teleport] Timed out waiting for NonAuthoritative position ({m_NonAuthoritativeTransform.GetSpaceRelativePosition()}) to teleport to position {teleportDestination}!");
SimulateOneFrame();
Assert.True(TeleportPositionMatches(nonAuthPosition), $"NonAuthoritative position ({m_NonAuthoritativeTransform.GetSpaceRelativePosition()}) is not the same as the destination position {teleportDestination}!");

var targetDistance = 0.0f;
if (!Approximately(m_DetectedPotentialInterpolatedTeleport, 0.0f))
{
Expand Down
2 changes: 1 addition & 1 deletion com.unity.netcode.gameobjects/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "com.unity.netcode.gameobjects",
"displayName": "Netcode for GameObjects",
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
"version": "1.3.1",
"version": "1.4.0",
"unity": "2020.3",
"dependencies": {
"com.unity.nuget.mono-cecil": "1.10.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ private bool VerifySpawnedObjectsMigrated()
return true;
}

private const int k_MaxObjectsToSpawn = 9;
/// <summary>
/// Integration test to verify that migrating NetworkObjects
/// into different scenes (in the same frame) is synchronized
Expand All @@ -167,7 +168,7 @@ private bool VerifySpawnedObjectsMigrated()
public IEnumerator MigrateIntoNewSceneTest()
{
// Spawn 9 NetworkObject instances
for (int i = 0; i < 9; i++)
for (int i = 0; i < k_MaxObjectsToSpawn; i++)
{
var serverInstance = Object.Instantiate(m_TestPrefab);
var serverNetworkObject = serverInstance.GetComponent<NetworkObject>();
Expand Down Expand Up @@ -204,12 +205,36 @@ public IEnumerator MigrateIntoNewSceneTest()
yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated);
AssertOnTimeout($"Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!");

// Verify that a late joining client synchronizes properly
// Register for the server-side client synchronization so we can send an object scene migration event at the same time
// the new client begins to synchronize
m_ServerNetworkManager.SceneManager.OnSynchronize += SceneManager_OnSynchronize;

// Verify that a late joining client synchronizes properly even while new scene migrations occur
// during its synchronization
yield return CreateAndStartNewClient();
yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated);

AssertOnTimeout($"[Late Joined Client] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!");
}

/// <summary>
/// Migrate objects into other scenes when a client begins synchronization
/// </summary>
/// <param name="clientId"></param>
private void SceneManager_OnSynchronize(ulong clientId)
{
var objectCount = k_MaxObjectsToSpawn - 1;

// Migrate the NetworkObjects into different scenes than they originally were migrated into
foreach (var scene in m_ScenesLoaded)
{
SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount].gameObject, scene);
SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount - 1].gameObject, scene);
SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount - 2].gameObject, scene);
objectCount -= 3;
}
}

/// <summary>
/// Integration test to verify changing the currently active scene
/// will migrate NetworkObjects with ActiveSceneSynchronization set
Expand Down