diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index d20f31b0b..e150b1ea0 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -861,7 +861,7 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) DeleteNetworkObject(obj); } -uint32 NetworkReplicator::GetObjectOwnerClientId(ScriptingObject* obj) +uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) { uint32 id = NetworkManager::ServerClientId; if (obj) @@ -870,11 +870,23 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(ScriptingObject* obj) const auto it = Objects.Find(obj->GetID()); if (it != Objects.End()) id = it->Item.OwnerClientId; + else + { + for (const SpawnItem& item : SpawnQueue) + { + if (item.Object == obj) + { + if (item.HasOwnership) + id = item.OwnerClientId; + break; + } + } + } } return id; } -NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj) +NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj) { NetworkObjectRole role = NetworkObjectRole::None; if (obj) @@ -883,6 +895,18 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj) const auto it = Objects.Find(obj->GetID()); if (it != Objects.End()) role = it->Item.Role; + else + { + for (const SpawnItem& item : SpawnQueue) + { + if (item.Object == obj) + { + if (item.HasOwnership) + role = item.Role; + break; + } + } + } } return role; } @@ -901,6 +925,18 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli auto& item = SpawnQueue[i]; if (item.Object == obj) { +#if !BUILD_RELEASE + if (ownerClientId == NetworkManager::LocalClientId) + { + // Ensure local client owns that object actually + CHECK(localRole == NetworkObjectRole::OwnedAuthoritative); + } + else + { + // Ensure local client doesn't own that object since it's owned by other client + CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); + } +#endif item.HasOwnership = true; item.HierarchicalOwnership = hierarchical; item.OwnerClientId = ownerClientId; @@ -1469,8 +1505,8 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl } // Recreate object locally (spawn only root) - ScriptingObject* obj = nullptr; Actor* prefabInstance = nullptr; + Array objects; if (msgData.PrefabId.IsValid()) { const NetworkReplicatedObject* parent = ResolveObject(rootItem.ParentId); @@ -1489,7 +1525,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl { if (Objects.Contains(child->GetID())) { - obj = FindPrefabObject(child, rootItem.PrefabObjectID); + ScriptingObject* obj = FindPrefabObject(child, rootItem.PrefabObjectID); if (Objects.Contains(obj->GetID())) { // Other instance with already spawned network object @@ -1521,46 +1557,77 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl return; } } - if (!obj) - obj = FindPrefabObject(prefabInstance, rootItem.PrefabObjectID); - if (!obj) + + // Resolve objects from prefab instance + objects.Resize(msgData.ItemsCount); + for (int32 i = 0; i < msgData.ItemsCount; i++) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", rootItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString()); - Delete(prefabInstance); - return; + auto& msgDataItem = msgDataItems[i]; + ScriptingObject* obj = FindPrefabObject(prefabInstance, msgDataItem.PrefabObjectID); + if (!obj) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", msgDataItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString()); + Delete(prefabInstance); + return; + } + objects[i] = obj; } } - else + else if (msgData.ItemsCount == 1) { // Spawn object - if (msgData.ItemsCount != 1) - { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Only prefab object spawning can contain more than one object (for type {})", String(rootItem.ObjectTypeName)); - return; - } const ScriptingTypeHandle objectType = Scripting::FindScriptingType(rootItem.ObjectTypeName); - obj = ScriptingObject::NewObject(objectType); + ScriptingObject* obj = ScriptingObject::NewObject(objectType); if (!obj) { NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to spawn object type {}", String(rootItem.ObjectTypeName)); return; } + objects.Add(obj); + } + else + { + // Spawn objects + objects.Resize(msgData.ItemsCount); + for (int32 i = 0; i < msgData.ItemsCount; i++) + { + auto& msgDataItem = msgDataItems[i]; + const ScriptingTypeHandle objectType = Scripting::FindScriptingType(msgDataItem.ObjectTypeName); + ScriptingObject* obj = ScriptingObject::NewObject(objectType); + if (!obj) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to spawn object type {}", String(msgDataItem.ObjectTypeName)); + for (ScriptingObject* e : objects) + Delete(e); + return; + } + objects[i] = obj; + if (i != 0) + { + // Link hierarchy of spawned objects before calling any networking code for them + if (auto sceneObject = ScriptingObject::Cast(obj)) + { + Actor* parent = nullptr; + for (int32 j = 0; j < i; j++) + { + if (msgDataItems[j].ObjectId == msgDataItem.ParentId) + { + parent = ScriptingObject::Cast(objects[j]); + break; + } + } + if (parent) + sceneObject->SetParent(parent); + } + } + } } // Setup all newly spawned objects for (int32 i = 0; i < msgData.ItemsCount; i++) { auto& msgDataItem = msgDataItems[i]; - if (i != 0) - { - obj = FindPrefabObject(prefabInstance, msgDataItem.PrefabObjectID); - if (!obj) - { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", msgDataItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString()); - Delete(prefabInstance); - return; - } - } + ScriptingObject* obj = objects[i]; if (!obj->IsRegistered()) obj->RegisterObject(); const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId); diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index e0893cc64..389350e5f 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -104,14 +104,14 @@ public: /// /// The network object. /// The Client Id. - API_FUNCTION() static uint32 GetObjectOwnerClientId(ScriptingObject* obj); + API_FUNCTION() static uint32 GetObjectOwnerClientId(const ScriptingObject* obj); /// /// Gets the role of the network object used locally (eg. to check if can simulate object). /// /// The network object. /// The object role. - API_FUNCTION() static NetworkObjectRole GetObjectRole(ScriptingObject* obj); + API_FUNCTION() static NetworkObjectRole GetObjectRole(const ScriptingObject* obj); /// /// Checks if the network object is owned locally (thus current client has authority to manage it). @@ -119,7 +119,7 @@ public: /// Equivalent to GetObjectRole == OwnedAuthoritative. /// The network object. /// True if object is owned by this client, otherwise false. - API_FUNCTION() FORCE_INLINE static bool IsObjectOwned(ScriptingObject* obj) + API_FUNCTION() FORCE_INLINE static bool IsObjectOwned(const ScriptingObject* obj) { return GetObjectRole(obj) == NetworkObjectRole::OwnedAuthoritative; } @@ -130,7 +130,7 @@ public: /// Equivalent to GetObjectRole != Replicated. /// The network object. /// True if object is simulated on this client, otherwise false. - API_FUNCTION() FORCE_INLINE static bool IsObjectSimulated(ScriptingObject* obj) + API_FUNCTION() FORCE_INLINE static bool IsObjectSimulated(const ScriptingObject* obj) { return GetObjectRole(obj) != NetworkObjectRole::Replicated; } @@ -141,7 +141,7 @@ public: /// Equivalent to (GetObjectRole == Replicated or GetObjectRole == ReplicatedAutonomous). /// The network object. /// True if object is simulated on this client, otherwise false. - API_FUNCTION() FORCE_INLINE static bool IsObjectReplicated(ScriptingObject* obj) + API_FUNCTION() FORCE_INLINE static bool IsObjectReplicated(const ScriptingObject* obj) { const NetworkObjectRole role = GetObjectRole(obj); return role == NetworkObjectRole::Replicated || role == NetworkObjectRole::ReplicatedSimulated;