Add NetworkRpcParams for sending RPC to specific set of clients or to read sender id
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@ struct NetworkConnection;
|
||||
struct NetworkMessage;
|
||||
struct NetworkConfig;
|
||||
struct NetworkDriverStats;
|
||||
struct NetworkRpcParams;
|
||||
|
||||
@@ -13,7 +13,7 @@ using Mono.Cecil.Cil;
|
||||
namespace Flax.Build.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Flax.Build plugin for Networking extenrions support. Generates required bindings glue code for automatic types replication and RPCs invoking.
|
||||
/// Flax.Build plugin for Networking extensions support. Generates required bindings glue code for automatic types replication and RPCs invoking.
|
||||
/// </summary>
|
||||
/// <seealso cref="Flax.Build.Plugin" />
|
||||
internal sealed class NetworkingPlugin : Plugin
|
||||
@@ -231,9 +231,16 @@ namespace Flax.Build.Plugins
|
||||
var arg = functionInfo.Parameters[i];
|
||||
if (i != 0)
|
||||
argNames += ", ";
|
||||
argNames += arg.Name;
|
||||
|
||||
|
||||
// Special handling of Rpc Params
|
||||
if (!arg.Type.IsPtr && arg.Type.Type == "NetworkRpcParams")
|
||||
{
|
||||
argNames += "NetworkRpcParams(stream)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Deserialize arguments
|
||||
argNames += arg.Name;
|
||||
contents.AppendLine($" {arg.Type.Type} {arg.Name};");
|
||||
contents.AppendLine($" stream->Read({arg.Name});");
|
||||
}
|
||||
@@ -250,16 +257,24 @@ namespace Flax.Build.Plugins
|
||||
contents.Append(" static void ").Append(functionInfo.Name).AppendLine("_Invoke(ScriptingObject* obj, void** args)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();");
|
||||
contents.AppendLine(" Span<uint32> targetIds;");
|
||||
for (int i = 0; i < functionInfo.Parameters.Count; i++)
|
||||
{
|
||||
var arg = functionInfo.Parameters[i];
|
||||
|
||||
// Special handling of Rpc Params
|
||||
if (!arg.Type.IsPtr && arg.Type.Type == "NetworkRpcParams")
|
||||
{
|
||||
contents.AppendLine($" targetIds = ((NetworkRpcParams*)args[{i}])->TargetIds;");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Serialize arguments
|
||||
contents.AppendLine($" stream->Write(*({arg.Type.Type}*)args[{i}]);");
|
||||
}
|
||||
|
||||
// Invoke RPC
|
||||
contents.AppendLine($" NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream);");
|
||||
contents.AppendLine($" NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream, targetIds);");
|
||||
contents.AppendLine(" }");
|
||||
}
|
||||
contents.AppendLine();
|
||||
@@ -1403,6 +1418,22 @@ namespace Flax.Build.Plugins
|
||||
{
|
||||
var parameter = method.Parameters[i];
|
||||
var parameterType = parameter.ParameterType;
|
||||
|
||||
// Special handling of Rpc Params
|
||||
if (string.Equals(parameterType.FullName, "FlaxEngine.Networking.NetworkRpcParams", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// new NetworkRpcParams { SenderId = networkStream.SenderId }
|
||||
il.Emit(OpCodes.Ldloca_S, (byte)(argsStart + i));
|
||||
il.Emit(OpCodes.Initobj, parameterType);
|
||||
il.Emit(OpCodes.Ldloca_S, (byte)(argsStart + i));
|
||||
il.Emit(OpCodes.Ldloc_1);
|
||||
var getSenderId = networkStreamType.Resolve().GetMethod("get_SenderId");
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(getSenderId));
|
||||
var senderId = parameterType.Resolve().GetField("SenderId");
|
||||
il.Emit(OpCodes.Stfld, module.ImportReference(senderId));
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateDotNetRPCSerializerType(ref context, type, false, argsStart + i, parameterType, il, networkStream.Resolve(), 1, null);
|
||||
}
|
||||
|
||||
@@ -1432,6 +1463,8 @@ namespace Flax.Build.Plugins
|
||||
il.Body.InitLocals = true;
|
||||
var varsStart = il.Body.Variables.Count;
|
||||
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
||||
|
||||
// Is Server/Is Client boolean constants
|
||||
il.Body.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [0]
|
||||
il.Body.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [1]
|
||||
@@ -1476,14 +1509,25 @@ namespace Flax.Build.Plugins
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Stloc, streamLocalIndex)); // stream loc=3
|
||||
|
||||
// Serialize all RPC parameters
|
||||
var targetIdsArgIndex = -1;
|
||||
FieldDefinition targetIdsField = null;
|
||||
for (int i = 0; i < method.Parameters.Count; i++)
|
||||
{
|
||||
var parameter = method.Parameters[i];
|
||||
var parameterType = parameter.ParameterType;
|
||||
|
||||
// Special handling of Rpc Params
|
||||
if (string.Equals(parameterType.FullName, "FlaxEngine.Networking.NetworkRpcParams", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
targetIdsArgIndex = i + 1; // NetworkRpcParams value argument index (starts at 1, 0 holds this)
|
||||
targetIdsField = parameterType.Resolve().GetField("TargetIds");
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateDotNetRPCSerializerType(ref context, type, true, i + 1, parameterType, il, networkStream.Resolve(), streamLocalIndex, ilStart);
|
||||
}
|
||||
|
||||
// NetworkReplicator.EndInvokeRPC(this, typeof(<type>), "<name>", stream);
|
||||
// NetworkReplicator.EndInvokeRPC(this, typeof(<type>), "<name>", stream, targetIds);
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Nop));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg_0));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldtoken, type));
|
||||
@@ -1492,7 +1536,14 @@ namespace Flax.Build.Plugins
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(getTypeFromHandle)));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldstr, method.Name));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex));
|
||||
var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 4);
|
||||
if (targetIdsArgIndex != -1)
|
||||
{
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, targetIdsArgIndex));
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldfld, module.ImportReference(targetIdsField)));
|
||||
}
|
||||
else
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Ldnull));
|
||||
var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5);
|
||||
il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(endInvokeRPC)));
|
||||
|
||||
// if (server && networkMode == NetworkManagerMode.Client) return;
|
||||
|
||||
Reference in New Issue
Block a user