This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs
Unity Technologies 63c7e4c78a com.unity.netcode.gameobjects@2.0.0-exp.4
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [2.0.0-exp.4] - 2024-05-31

### Added

- Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933)
- Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912)
- Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912)
- Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912)

### Fixed

- Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933)
- Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924)
- Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912)
- Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898)

### Changed

- Change all the access modifiers of test class from Public to Internal (#2930)
- Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929)
- Changed `NetworkClient.SessionModeTypes` to `NetworkClient.NetworkTopologyTypes`. (#2875)
- Changed `NetworkClient.SessionModeType` to `NetworkClient.NetworkTopologyType`. (#2875)
- Changed `NetworkConfig.SessionMode` to `NeworkConfig.NetworkTopology`. (#2875)
2024-05-31 00:00:00 +00:00

312 lines
14 KiB
C#

using System.Linq;
using System.Runtime.CompilerServices;
namespace Unity.Netcode
{
internal struct CreateObjectMessage : INetworkMessage
{
public int Version => 0;
private const string k_Name = "CreateObjectMessage";
public NetworkObject.SceneObject ObjectInfo;
private FastBufferReader m_ReceivedNetworkVariableData;
// DA - NGO CMB SERVICE NOTES:
// The ObserverIds and ExistingObserverIds will only be populated if k_UpdateObservers is set
// ObserverIds is the full list of observers (see below)
internal ulong[] ObserverIds;
// While this does consume a bit more bandwidth, this is only sent by the authority/owner
// and can be used to determine which clients should receive the ObjectInfo serialized data.
// All other already existing observers just need to receive the NewObserverIds and the
// NetworkObjectId
internal ulong[] NewObserverIds;
// If !IncludesSerializedObject then the NetworkObjectId will be serialized.
// This happens when we are just sending an update to the observers list
// to clients that already have the NetworkObject spawned
internal ulong NetworkObjectId;
private const byte k_IncludesSerializedObject = 0x01;
private const byte k_UpdateObservers = 0x02;
private const byte k_UpdateNewObservers = 0x04;
private byte m_CreateObjectMessageTypeFlags;
internal bool IncludesSerializedObject
{
get
{
return GetFlag(k_IncludesSerializedObject);
}
set
{
SetFlag(value, k_IncludesSerializedObject);
}
}
internal bool UpdateObservers
{
get
{
return GetFlag(k_UpdateObservers);
}
set
{
SetFlag(value, k_UpdateObservers);
}
}
internal bool UpdateNewObservers
{
get
{
return GetFlag(k_UpdateNewObservers);
}
set
{
SetFlag(value, k_UpdateNewObservers);
}
}
private bool GetFlag(int flag)
{
return (m_CreateObjectMessageTypeFlags & flag) != 0;
}
private void SetFlag(bool set, byte flag)
{
if (set) { m_CreateObjectMessageTypeFlags = (byte)(m_CreateObjectMessageTypeFlags | flag); }
else { m_CreateObjectMessageTypeFlags = (byte)(m_CreateObjectMessageTypeFlags & ~flag); }
}
public void Serialize(FastBufferWriter writer, int targetVersion)
{
writer.WriteValueSafe(m_CreateObjectMessageTypeFlags);
if (UpdateObservers)
{
BytePacker.WriteValuePacked(writer, ObserverIds.Length);
foreach (var clientId in ObserverIds)
{
BytePacker.WriteValuePacked(writer, clientId);
}
}
if (UpdateNewObservers)
{
BytePacker.WriteValuePacked(writer, NewObserverIds.Length);
foreach (var clientId in NewObserverIds)
{
BytePacker.WriteValuePacked(writer, clientId);
}
}
if (IncludesSerializedObject)
{
ObjectInfo.Serialize(writer);
}
else
{
BytePacker.WriteValuePacked(writer, NetworkObjectId);
}
}
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
{
var networkManager = (NetworkManager)context.SystemOwner;
if (!networkManager.IsClient)
{
return false;
}
reader.ReadValueSafe(out m_CreateObjectMessageTypeFlags);
if (UpdateObservers)
{
var length = 0;
ByteUnpacker.ReadValuePacked(reader, out length);
ObserverIds = new ulong[length];
var clientId = (ulong)0;
for (int i = 0; i < length; i++)
{
ByteUnpacker.ReadValuePacked(reader, out clientId);
ObserverIds[i] = clientId;
}
}
if (UpdateNewObservers)
{
var length = 0;
ByteUnpacker.ReadValuePacked(reader, out length);
NewObserverIds = new ulong[length];
var clientId = (ulong)0;
for (int i = 0; i < length; i++)
{
ByteUnpacker.ReadValuePacked(reader, out clientId);
NewObserverIds[i] = clientId;
}
}
if (IncludesSerializedObject)
{
ObjectInfo.Deserialize(reader);
}
else
{
ByteUnpacker.ReadValuePacked(reader, out NetworkObjectId);
}
if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo))
{
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Hash, reader, ref context, k_Name);
return false;
}
m_ReceivedNetworkVariableData = reader;
return true;
}
public void Handle(ref NetworkContext context)
{
var networkManager = (NetworkManager)context.SystemOwner;
// If a client receives a create object message and it is still synchronizing, then defer the object creation until it has finished synchronizing
if (networkManager.SceneManager.ShouldDeferCreateObject())
{
networkManager.SceneManager.DeferCreateObject(context.SenderId, context.MessageSize, ObjectInfo, m_ReceivedNetworkVariableData, ObserverIds, NewObserverIds);
}
else
{
if (networkManager.DistributedAuthorityMode && !IncludesSerializedObject && UpdateObservers)
{
ObjectInfo = new NetworkObject.SceneObject()
{
NetworkObjectId = NetworkObjectId,
};
}
CreateObject(ref networkManager, context.SenderId, context.MessageSize, ObjectInfo, m_ReceivedNetworkVariableData, ObserverIds, NewObserverIds);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CreateObject(ref NetworkManager networkManager, ref NetworkSceneManager.DeferredObjectCreation deferredObjectCreation)
{
var senderId = deferredObjectCreation.SenderId;
var observerIds = deferredObjectCreation.ObserverIds;
var newObserverIds = deferredObjectCreation.NewObserverIds;
var messageSize = deferredObjectCreation.MessageSize;
var sceneObject = deferredObjectCreation.SceneObject;
var networkVariableData = deferredObjectCreation.FastBufferReader;
CreateObject(ref networkManager, senderId, messageSize, sceneObject, networkVariableData, observerIds, newObserverIds);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CreateObject(ref NetworkManager networkManager, ulong senderId, uint messageSize, NetworkObject.SceneObject sceneObject, FastBufferReader networkVariableData, ulong[] observerIds, ulong[] newObserverIds)
{
var networkObject = (NetworkObject)null;
try
{
if (!networkManager.DistributedAuthorityMode)
{
networkObject = NetworkObject.AddSceneObject(sceneObject, networkVariableData, networkManager);
}
else
{
var hasObserverIdList = observerIds != null && observerIds.Length > 0;
var hasNewObserverIdList = newObserverIds != null && newObserverIds.Length > 0;
// Depending upon visibility of the NetworkObject and the client in question, it could be that
// this client already has visibility of this NetworkObject
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(sceneObject.NetworkObjectId))
{
// If so, then just get the local instance
networkObject = networkManager.SpawnManager.SpawnedObjects[sceneObject.NetworkObjectId];
// This should not happen, logging error just in case
if (hasNewObserverIdList && newObserverIds.Contains(networkManager.LocalClientId))
{
NetworkLog.LogErrorServer($"[{nameof(CreateObjectMessage)}][Duplicate-Broadcast] Detected duplicated object creation for {sceneObject.NetworkObjectId}!");
}
else // Trap to make sure the owner is not receiving any messages it sent
if (networkManager.CMBServiceConnection && networkManager.LocalClientId == networkObject.OwnerClientId)
{
NetworkLog.LogWarning($"[{nameof(CreateObjectMessage)}][Client-{networkManager.LocalClientId}][Duplicate-CreateObjectMessage][Client Is Owner] Detected duplicated object creation for {networkObject.name}-{sceneObject.NetworkObjectId}!");
}
}
else
{
networkObject = NetworkObject.AddSceneObject(sceneObject, networkVariableData, networkManager, true);
}
// DA - NGO CMB SERVICE NOTES:
// It is possible for two clients to connect at the exact same time which, due to client-side spawning, can cause each client
// to miss their spawns. For now, all player NetworkObject spawns will always be visible to all known connected clients
var clientList = hasObserverIdList && !networkObject.IsPlayerObject ? observerIds : networkManager.ConnectedClientsIds;
// Update the observers for this instance
foreach (var clientId in clientList)
{
networkObject.Observers.Add(clientId);
}
// Mock CMB Service and forward to all clients
if (networkManager.DAHost)
{
// DA - NGO CMB SERVICE NOTES:
// (*** See above notes fist ***)
// If it is a player object freshly spawning and one or more clients all connect at the exact same time (i.e. received on effectively
// the same frame), then we need to check the observers list to make sure all players are visible upon first spawning. At a later date,
// for area of interest we will need to have some form of follow up "observer update" message to cull out players not within each
// player's AOI.
if (networkObject.IsPlayerObject && hasNewObserverIdList && clientList.Count != observerIds.Length)
{
// For same-frame newly spawned players that might not be aware of all other players, update the player's observer
// list.
observerIds = clientList.ToArray();
}
var createObjectMessage = new CreateObjectMessage()
{
ObjectInfo = sceneObject,
m_ReceivedNetworkVariableData = networkVariableData,
ObserverIds = hasObserverIdList ? observerIds : null,
NetworkObjectId = networkObject.NetworkObjectId,
IncludesSerializedObject = true,
};
foreach (var clientId in clientList)
{
// DA - NGO CMB SERVICE NOTES:
// If the authority did not specify the list of clients and the client is not an observer or we are the owner/originator
// or we are the DAHost, then we skip sending the message.
if ((!hasObserverIdList && (!networkObject.Observers.Contains(clientId)) ||
clientId == networkObject.OwnerClientId || clientId == NetworkManager.ServerClientId))
{
continue;
}
// DA - NGO CMB SERVICE NOTES:
// If this included a list of new observers and the targeted clientId is one of the observers, then send the serialized data.
// Otherwise, the targeted clientId has already has visibility (i.e. it is already spawned) and so just send the updated
// observers list to that client's instance.
createObjectMessage.IncludesSerializedObject = hasNewObserverIdList && newObserverIds.Contains(clientId);
networkManager.SpawnManager.SendSpawnCallForObject(clientId, networkObject);
}
}
}
if (networkObject != null)
{
networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject, messageSize);
}
}
catch (System.Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
}
}