From f5c9dce34a459e8616549bbcf96487c8177c76c2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 16 Apr 2023 18:55:31 +0200 Subject: [PATCH] Fix network RPC object id mapping back to server id when sent from client --- .../Engine/Networking/NetworkReplicator.cpp | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 98c7c2432..d20f31b0b 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -32,7 +32,9 @@ #if !BUILD_RELEASE bool NetworkReplicator::EnableLog = false; #include "Engine/Core/Log.h" +#include "Engine/Content/Content.h" #define NETWORK_REPLICATOR_LOG(messageType, format, ...) if (NetworkReplicator::EnableLog) { LOG(messageType, format, ##__VA_ARGS__); } +#define USE_NETWORK_REPLICATOR_LOG 1 #else #define NETWORK_REPLICATOR_LOG(messageType, format, ...) #endif @@ -105,10 +107,15 @@ struct NetworkReplicatedObject uint32 OwnerClientId; uint32 LastOwnerFrame = 0; NetworkObjectRole Role; - uint8 Spawned = false; + uint8 Spawned : 1; DataContainer TargetClientIds; INetworkObject* AsNetworkObject; + NetworkReplicatedObject() + { + Spawned = 0; + } + bool operator==(const NetworkReplicatedObject& other) const { return Object == other.Object; @@ -524,6 +531,36 @@ void SetupObjectSpawnGroupItem(ScriptingObject* obj, ArrayItems.Add(&spawnItem); } +void FindObjectsForSpawn(SpawnGroup& group, ChunkedArray& spawnItems, ScriptingObject* obj) +{ + // Add any registered network objects + auto it = Objects.Find(obj->GetID()); + if (it != Objects.End()) + { + auto& item = it->Item; + if (!item.Spawned) + { + // One of the parents of this object is being spawned so spawn it too + item.Spawned = true; + auto& spawnItem = spawnItems.AddOne(); + spawnItem.Object = obj; + spawnItem.Targets.Link(item.TargetClientIds); + spawnItem.OwnerClientId = item.OwnerClientId; + spawnItem.Role = item.Role; + group.Items.Add(&spawnItem); + } + } + + // Iterate over children + if (auto* actor = ScriptingObject::Cast(obj)) + { + for (auto* script : actor->Scripts) + FindObjectsForSpawn(group, spawnItems, script); + for (auto* child : actor->Children) + FindObjectsForSpawn(group, spawnItems, child); + } +} + void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject* obj) { // TODO: implement objects state replication frequency and dirtying @@ -1159,9 +1196,16 @@ void NetworkInternal::NetworkReplicatorUpdate() } // Spawn groups of objects + ChunkedArray spawnItems; for (SpawnGroup& g : spawnGroups) { + // Include any added objects within spawn group that were not spawned manually (eg. AddObject for script/actor attached to spawned actor) + ScriptingObject* groupRoot = g.Items[0]->Object.Get(); + FindObjectsForSpawn(g, spawnItems, groupRoot); + SendObjectSpawnMessage(g, NetworkManager::Clients); + + spawnItems.Clear(); } SpawnQueue.Clear(); } @@ -1325,7 +1369,7 @@ void NetworkInternal::NetworkReplicatorUpdate() if (e.Info.Server && isClient) { // Client -> Server -#if !BUILD_RELEASE +#if USE_NETWORK_REPLICATOR_LOG if (e.Targets.Length() != 0) NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); #endif @@ -1520,10 +1564,6 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl if (!obj->IsRegistered()) obj->RegisterObject(); const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId); - if (!parent && msgDataItem.ParentId.IsValid()) - { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} as parent to spawned object", msgDataItem.ParentId.ToString()); - } // Add object to the list NetworkReplicatedObject item; @@ -1554,6 +1594,21 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl sceneObject->SetParent(parent->Object.As()); else if (auto* parentActor = Scripting::TryFindObject(msgDataItem.ParentId)) sceneObject->SetParent(parentActor); + else if (msgDataItem.ParentId.IsValid()) + { +#if USE_NETWORK_REPLICATOR_LOG + // Ignore case when parent object in a message was a scene (eg. that is already unloaded on a client) + AssetInfo assetInfo; + if (!Content::GetAssetInfo(msgDataItem.ParentId, assetInfo) || assetInfo.TypeName == TEXT("FlaxEngine.SceneAsset")) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} as parent to spawned object", msgDataItem.ParentId.ToString()); + } +#endif + } + } + else if (!parent && msgDataItem.ParentId.IsValid()) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} as parent to spawned object", msgDataItem.ParentId.ToString()); } if (item.AsNetworkObject) @@ -1657,7 +1712,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name); if (!info) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgData.ObjectId, String(msgData.RpcTypeName), String(msgData.RpcName)); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(msgData.RpcTypeName), String(msgData.RpcName), msgData.ObjectId); return; }