Add NetworkRpcParams for sending RPC to specific set of clients or to read sender id

This commit is contained in:
Wojtek Figat
2023-04-15 12:04:40 +02:00
parent a52b352bd9
commit e4804db160
6 changed files with 143 additions and 19 deletions

View File

@@ -178,6 +178,7 @@ struct RpcItem
NetworkRpcName Name;
NetworkRpcInfo Info;
BytesContainer ArgsData;
DataContainer<uint32> Targets;
};
namespace
@@ -329,6 +330,46 @@ void BuildCachedTargets(const Array<NetworkClient*>& clients, const DataContaine
}
}
void BuildCachedTargets(const Array<NetworkClient*>& clients, const DataContainer<uint32>& clientIds1, const Span<uint32>& clientIds2, const uint32 excludedClientId = NetworkManager::ServerClientId)
{
CachedTargets.Clear();
if (clientIds1.IsValid())
{
if (clientIds2.IsValid())
{
for (const NetworkClient* client : clients)
{
if (client->State == NetworkConnectionState::Connected && client->ClientId != excludedClientId)
{
for (int32 i = 0; i < clientIds1.Length(); i++)
{
if (clientIds1[i] == client->ClientId)
{
for (int32 j = 0; j < clientIds2.Length(); j++)
{
if (clientIds2[j] == client->ClientId)
{
CachedTargets.Add(client->Connection);
break;
}
}
break;
}
}
}
}
}
else
{
BuildCachedTargets(clients, clientIds1, excludedClientId);
}
}
else
{
BuildCachedTargets(clients, clientIds2, excludedClientId);
}
}
FORCE_INLINE void BuildCachedTargets(const NetworkReplicatedObject& item)
{
// By default send object to all connected clients excluding the owner but with optional TargetClientIds list
@@ -560,6 +601,11 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b
DirtyObjectImpl(item, obj);
}
NetworkRpcParams::NetworkRpcParams(const NetworkStream* stream)
: SenderId(stream->SenderId)
{
}
#if !COMPILE_WITHOUT_CSHARP
#include "Engine/Scripting/ManagedCLR/MUtils.h"
@@ -601,9 +647,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
}
void NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream)
void NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MonoArray* targetIds)
{
EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream);
EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
}
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
@@ -886,7 +932,7 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
return CachedWriteStream;
}
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream)
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
{
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
if (!info || !obj || NetworkManager::IsOffline())
@@ -897,8 +943,8 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
rpc.Name.First = type;
rpc.Name.Second = name;
rpc.Info = *info;
const Span<byte> argsData(argsStream->GetBuffer(), argsStream->GetPosition());
rpc.ArgsData.Copy(argsData);
rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition()));
rpc.Targets.Copy(targetIds);
#if USE_EDITOR || !BUILD_RELEASE
auto it = Objects.Find(obj->GetID());
if (it == Objects.End())
@@ -1279,12 +1325,16 @@ void NetworkInternal::NetworkReplicatorUpdate()
if (e.Info.Server && isClient)
{
// Client -> Server
#if !BUILD_RELEASE
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
peer->EndSendMessage(channel, msg);
}
else if (e.Info.Client && (isServer || isHost))
{
// Server -> Client(s)
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, NetworkManager::LocalClientId);
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
peer->EndSendMessage(channel, msg, CachedTargets);
}
}
@@ -1310,7 +1360,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
if (client && item.OwnerClientId != client->ClientId)
return;
const uint32 senderClientId = client ? client->ClientId : NetworkManager::LocalClientId;
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
if (msgData.PartsCount == 1)
{
// Replicate
@@ -1333,7 +1383,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, N
if (DespawnedObjects.Contains(msgData.ObjectId))
return; // Skip replicating not-existing objects
const uint32 senderClientId = client ? client->ClientId : NetworkManager::LocalClientId;
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
AddObjectReplicateItem(event, msgData, msgData.PartStart, msgData.PartSize, senderClientId);
}
@@ -1627,7 +1677,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
if (CachedReadStream == nullptr)
CachedReadStream = New<NetworkStream>();
NetworkStream* stream = CachedReadStream;
stream->SenderId = client ? client->ClientId : NetworkManager::LocalClientId;
stream->SenderId = client ? client->ClientId : NetworkManager::ServerClientId;
stream->Initialize(event.Message.Buffer + event.Message.Position, msgData.ArgsSize);
// Execute RPC

View File

@@ -109,10 +109,11 @@ namespace FlaxEngine.Networking
/// <param name="type">The RPC type.</param>
/// <param name="name">The RPC name.</param>
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
[Unmanaged]
public static void EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream)
public static void EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
{
Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream));
Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
}
/// <summary>

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Types.h"
#include "Engine/Core/Types/Span.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Scripting/ScriptingType.h"
@@ -175,13 +176,14 @@ public:
/// <param name="type">The RPC type.</param>
/// <param name="name">The RPC name.</param>
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream);
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
private:
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
API_FUNCTION(NoProxy) static void CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream);
API_FUNCTION(NoProxy) static void CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MonoArray* targetIds);
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
#endif
};

View File

@@ -5,6 +5,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Types/Span.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Scripting/ScriptingType.h"
@@ -12,6 +13,24 @@
class NetworkStream;
// Additional context parameters for Network RPC execution (eg. to identify who sends the data).
API_STRUCT(NoDefault, Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkRpcParams
{
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkRpcParams);
NetworkRpcParams() = default;
NetworkRpcParams(const NetworkStream* stream);
/// <summary>
/// The ClientId of the network client that is a data sender. Can be used to detect who send the incoming RPC or replication data. Ignored when sending data.
/// </summary>
API_FIELD() uint32 SenderId = 0;
/// <summary>
/// The list of ClientId of the network clients that should receive RPC. Can be used to send RPC to a specific client(s). Ignored when receiving data.
/// </summary>
API_FIELD() Span<uint32> TargetIds;
};
// Network RPC identifier name (pair of type and function name)
typedef Pair<ScriptingTypeHandle, StringAnsiView> NetworkRpcName;

View File

@@ -17,3 +17,4 @@ struct NetworkConnection;
struct NetworkMessage;
struct NetworkConfig;
struct NetworkDriverStats;
struct NetworkRpcParams;