diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 02ac5cf07..214b26687 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -123,6 +123,7 @@ struct SpawnItem ScriptingObjectReference Object; DataContainer Targets; bool HasOwnership = false; + bool HierarchicalOwnership = false; uint32 OwnerClientId; NetworkObjectRole Role; }; @@ -329,6 +330,13 @@ FORCE_INLINE void DeleteNetworkObject(ScriptingObject* obj) obj->DeleteObject(); } +bool IsParentOf(ScriptingObject* obj, ScriptingObject* parent) +{ + if (const auto* sceneObject = ScriptingObject::Cast(obj)) + return sceneObject->GetParent() == parent || IsParentOf(sceneObject->GetParent(), parent); + return false; +} + SceneObject* FindPrefabObject(Actor* a, const Guid& prefabObjectId) { if (a->GetPrefabObjectID() == prefabObjectId) @@ -500,9 +508,9 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) DeleteNetworkObject(obj); } -uint32 NetworkReplicator::GetObjectClientId(ScriptingObject* obj) +uint32 NetworkReplicator::GetObjectOwnerClientId(ScriptingObject* obj) { - uint32 id = 0; + uint32 id = NetworkManager::ServerClientId; if (obj) { ScopeLock lock(ObjectsLock); @@ -526,7 +534,7 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj) return role; } -void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole) +void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole, bool hierarchical) { if (!obj) return; @@ -541,6 +549,7 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli if (item.Object == obj) { item.HasOwnership = true; + item.HierarchicalOwnership = hierarchical; item.OwnerClientId = ownerClientId; item.Role = localRole; break; @@ -577,6 +586,16 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); item.Role = localRole; } + + // Go down hierarchy + if (hierarchical) + { + for (auto& e : Objects) + { + if (e.Item.ParentId == item.ObjectId) + SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical); + } + } } void NetworkReplicator::DirtyObject(ScriptingObject* obj) @@ -726,6 +745,22 @@ void NetworkInternal::NetworkReplicatorUpdate() { PROFILE_CPU_NAMED("SpawnQueue"); for (auto& e : SpawnQueue) + { + // Propagate hierarchical ownership from spawned parent to spawned child objects (eg. spawned script and spawned actor with set hierarchical ownership on actor which should affect script too) + if (e.HasOwnership && e.HierarchicalOwnership) + { + for (auto& q : SpawnQueue) + { + if (!q.HasOwnership && IsParentOf(q.Object, e.Object)) + { + q.HasOwnership = true; + q.Role = e.Role; + q.OwnerClientId = e.OwnerClientId; + } + } + } + } + for (auto& e : SpawnQueue) { ScriptingObject* obj = e.Object.Get(); auto it = Objects.Find(obj->GetID()); @@ -745,6 +780,8 @@ void NetworkInternal::NetworkReplicatorUpdate() { item.Role = e.Role; item.OwnerClientId = e.OwnerClientId; + if (e.HierarchicalOwnership) + NetworkReplicator::SetObjectOwnership(obj, e.OwnerClientId, e.Role, true); } if (e.Targets.IsValid()) { diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index 8fbff4ff9..237d69f84 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -96,7 +96,7 @@ public: /// /// The network object. /// The Client Id. - API_FUNCTION() static uint32 GetObjectClientId(ScriptingObject* obj); + API_FUNCTION() static uint32 GetObjectOwnerClientId(ScriptingObject* obj); /// /// Gets the role of the network object used locally (eg. to check if can simulate object). @@ -145,7 +145,8 @@ public: /// The network object. /// The new owner. Set to NetworkManager::LocalClientId for local client to be owner (server might reject it). /// The local role to assign for the object. - API_FUNCTION() static void SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole = NetworkObjectRole::Replicated); + /// True if apply the ownership to all child objects of this object (eg. all child actors and scripts attached to the networked actor). + API_FUNCTION() static void SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole = NetworkObjectRole::Replicated, bool hierarchical = false); /// /// Marks the object dirty to perform immediate replication to the other clients.