Fix hierarchical network ownership propagation to sub-objects

#1066
This commit is contained in:
Wojtek Figat
2023-05-16 14:58:16 +02:00
parent 64f3f1e9bc
commit 953ae3e9bb
2 changed files with 60 additions and 32 deletions

View File

@@ -745,7 +745,7 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle,
return false;
}
void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent)
void NetworkReplicator::AddObject(ScriptingObject* obj, const ScriptingObject* parent)
{
if (!obj || NetworkManager::IsOffline())
return;
@@ -774,6 +774,19 @@ void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent)
item.OwnerClientId = NetworkManager::ServerClientId; // Server owns objects by default
item.Role = NetworkManager::IsClient() ? NetworkObjectRole::Replicated : NetworkObjectRole::OwnedAuthoritative;
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Add new object {}:{}, parent {}:{}", item.ToString(), obj->GetType().ToString(), item.ParentId.ToString(), parent ? parent->GetType().ToString() : String::Empty);
for (const SpawnItem& spawnItem : SpawnQueue)
{
if (spawnItem.HasOwnership && spawnItem.HierarchicalOwnership)
{
if (IsParentOf(obj, spawnItem.Object))
{
// Inherit ownership
item.Role = spawnItem.Role;
item.OwnerClientId = spawnItem.OwnerClientId;
break;
}
}
}
Objects.Add(MoveTemp(item));
}
@@ -864,7 +877,7 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj)
uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
{
uint32 id = NetworkManager::ServerClientId;
if (obj)
if (obj && NetworkManager::IsConnected())
{
ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID());
@@ -896,7 +909,7 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
{
NetworkObjectRole role = NetworkObjectRole::None;
if (obj)
if (obj && NetworkManager::IsConnected())
{
ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID());
@@ -927,10 +940,11 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole, bool hierarchical)
{
if (!obj)
if (!obj || NetworkManager::IsOffline())
return;
const Guid objectId = obj->GetID();
ScopeLock lock(ObjectsLock);
const auto it = Objects.Find(obj->GetID());
const auto it = Objects.Find(objectId);
if (it == Objects.End())
{
// Special case if we're just spawning this object
@@ -958,31 +972,33 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
break;
}
}
return;
}
auto& item = it->Item;
if (item.Object != obj)
return;
// Check if this client is object owner
if (item.OwnerClientId == NetworkManager::LocalClientId)
{
// Check if object owner will change
if (item.OwnerClientId != ownerClientId)
{
// Change role locally
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
item.OwnerClientId = ownerClientId;
item.LastOwnerFrame = 1;
item.Role = localRole;
SendObjectRoleMessage(item);
}
}
else
{
// Allow to change local role of the object (except ownership)
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
item.Role = localRole;
auto& item = it->Item;
if (item.Object != obj)
return;
// Check if this client is object owner
if (item.OwnerClientId == NetworkManager::LocalClientId)
{
// Check if object owner will change
if (item.OwnerClientId != ownerClientId)
{
// Change role locally
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
item.OwnerClientId = ownerClientId;
item.LastOwnerFrame = 1;
item.Role = localRole;
SendObjectRoleMessage(item);
}
}
else
{
// Allow to change local role of the object (except ownership)
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
item.Role = localRole;
}
}
// Go down hierarchy
@@ -990,7 +1006,7 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
{
for (auto& e : Objects)
{
if (e.Item.ParentId == item.ObjectId)
if (e.Item.ParentId == objectId)
SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical);
}
}
@@ -1196,9 +1212,11 @@ void NetworkInternal::NetworkReplicatorUpdate()
{
if (!q.HasOwnership && IsParentOf(q.Object, e.Object))
{
// Inherit ownership
q.HasOwnership = true;
q.Role = e.Role;
q.OwnerClientId = e.OwnerClientId;
break;
}
}
}
@@ -1637,7 +1655,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
}
}
// Setup all newly spawned objects
// Add all newly spawned objects
for (int32 i = 0; i < msgData.ItemsCount; i++)
{
auto& msgDataItem = msgDataItems[i];
@@ -1666,6 +1684,16 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
// Boost future lookups by using indirection
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", msgDataItem.ObjectId, item.ToString(), obj->GetType().ToString());
IdsRemappingTable.Add(msgDataItem.ObjectId, item.ObjectId);
}
// Spawn all newly spawned objects (ensure to have valid ownership hierarchy set before spawning object)
for (int32 i = 0; i < msgData.ItemsCount; i++)
{
auto& msgDataItem = msgDataItems[i];
ScriptingObject* obj = objects[i];
auto it = Objects.Find(obj->GetID());
auto& item = it->Item;
const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId);
// Automatic parenting for scene objects
auto sceneObject = ScriptingObject::Cast<SceneObject>(obj);

View File

@@ -68,7 +68,7 @@ public:
/// <remarks>Does nothing if network is offline.</remarks>
/// <param name="obj">The object to replicate.</param>
/// <param name="parent">The parent of the object (eg. player that spawned it).</param>
API_FUNCTION() static void AddObject(ScriptingObject* obj, ScriptingObject* parent = nullptr);
API_FUNCTION() static void AddObject(ScriptingObject* obj, const ScriptingObject* parent = nullptr);
/// <summary>
/// Removes the object from the network replication system.
@@ -80,14 +80,14 @@ public:
/// <summary>
/// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab).
/// </summary>
/// <remarks>Does nothing if network is offline.</remarks>
/// <remarks>Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system.</remarks>
/// <param name="obj">The object to spawn on other clients.</param>
API_FUNCTION() static void SpawnObject(ScriptingObject* obj);
/// <summary>
/// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab).
/// </summary>
/// <remarks>Does nothing if network is offline.</remarks>
/// <remarks>Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system.</remarks>
/// <param name="obj">The object to spawn on other clients.</param>
/// <param name="clientIds">List with network client IDs that should receive network spawn event. Empty to spawn on all clients.</param>
API_FUNCTION() static void SpawnObject(ScriptingObject* obj, const DataContainer<uint32>& clientIds);