Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC manager rework #52

Draft
wants to merge 14 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions JM/CF/Scripts/3_Game/CommunityFramework/CommunityFramework.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class CommunityFramework
{
static CF_ObjectManager ObjectManager;
static CF_XML XML;
static CF_RPC RPC;

#ifdef CF_MODULE_PERMISSIONS
static ref CF_Permission_ManagerBase Permission;
Expand Down Expand Up @@ -53,6 +54,7 @@ class CommunityFramework
{
ObjectManager._Cleanup();
XML._Cleanup();
RPC._Cleanup();

#ifdef CF_MODULE_PERMISSIONS
Permission._Cleanup();
Expand Down
10 changes: 10 additions & 0 deletions JM/CF/Scripts/3_Game/CommunityFramework/RPC/DayZGame.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
modded class DayZGame
{
override void OnRPC(PlayerIdentity sender, Object target, int rpc_type, ParamsReadContext ctx)
{
//Intercept incoming RPCs and consume them if their rpc_type is meant for CF.RPC
if(CF_RPC._OnRPC(sender, target, rpc_type, ctx)) return;

super.OnRPC(sender, target, rpc_type, ctx);
}
}
125 changes: 125 additions & 0 deletions JM/CF/Scripts/3_Game/CommunityFramework/RPC/RPC.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
class CF_RPC
{
//!Single static instance. Do not create with new or spawn - use CF.RPC for access instead.
protected void CF_RPC();
protected void ~CF_RPC();

protected static ref map<string, Class> m_RegisteredInstances = new map<string, Class>();

static const int CF_RPC_SCRIPT_ID = 10043;

static const PlayerIdentity SERVER = NULL;

/**
* @brief Registers a handler instance for the RPC receiving machine. A handler is a class instance which RPC functions will be invoked on. Alternatively you can inherit your handler class from CF_RPC_HandlerBase which registers automatically.
* @code
* constructor()
* {
* CF.RPC.RegisterHandler(this);
* }
* @endcode
*
* @param instance The handler instance to be registered
* @return bool Returns true if successfully registered, false otherwise (see script log for details).
*/
static bool RegisterHandler(Class instance)
{
if(!m_RegisteredInstances.Contains(instance.ClassName()))
{
m_RegisteredInstances.Set(instance.ClassName(), instance);

return true;
}

PrintFormat("[WARNING][CommunityFramework][RPC] Failed to register the handler instance %1. An instance for the handler type %2 was already registered. See trace below:", instance, instance.ClassName());
DumpStack();

return false;
}

/**
* @brief Prepares a new RPC send context, which data can be written to using Write() and that is transmitted using SendTo()
* @code
* auto rpc = CF.RPC.Prepare("MyHandler", "MyFunction", true);
* rpc.Write("My data");
* rpc.SendTo(receiverIdentity1);
* rpc.SendTo(receiverIdentity2);
* @endcode
*
* @param handlerType Classtype of handler on the receiving machine as string.
* @param functionName The name of the function that shall be invoked in the handler instance on the receiving machine.
* @param guaranteed Guaranteed RPC delivery. True = Will arrive eventually. False = Might be dropped after first attempt.
* @param target Optional target object. Must be globally known in order to work (Map objects, objects spawned by the server globally). NULL by default.
* @return CF_RPC_Context RPC write and send context.
*/
static ref CF_RPC_Context Prepare(string handlerType, string functionName, bool guaranteed, Object target = null)
{
return CF_RPC_Context._Prepare(handlerType, functionName, guaranteed, target);
}

/**
* @brief [Internal] Handle incoming RPCs
*
* @return bool Returns if the RPC has been consumed by CF
*/
static bool _OnRPC(PlayerIdentity sender, Object target, int rpc_type, ParamsReadContext ctx)
{
if(rpc_type != CF_RPC_SCRIPT_ID) return false;

string handlerType, functionName;

if(ctx.Read(handlerType) && ctx.Read(functionName))
{
Class invoke = null;

if(m_RegisteredInstances.Contains(handlerType))
{
invoke = m_RegisteredInstances[handlerType];
}
else
{
/*
Inherited handlers will have the function the rpc is looking for, but might not match the class name.
The first matching instance is the result and will be cached for future rpcs of the handlerType.
*/
auto handlerTypename = handlerType.ToType();

foreach(auto key, auto value : m_RegisteredInstances)
{
if(key.ToType().IsInherited(handlerTypename))
{
invoke = value;
break;
}
}

if(invoke)
{
m_RegisteredInstances.Set(handlerType, invoke);
}
}

if(invoke)
{
g_Game.GameScript.CallFunctionParams(invoke, functionName, NULL, new Param3<ref ParamsReadContext, ref PlayerIdentity, Object>(ctx, sender, target));
}
else
{
PrintFormat("[ERROR][CommunityFramework][RPC] No instance found for handler type %1. RPC ignored...", handlerType);
}
}

return true;
}

/**
* @brief [Internal] CommunityFramework cleanup
*
* @return void
*/
static void _Cleanup()
{
m_RegisteredInstances.Clear();
delete m_RegisteredInstances;
}
}
63 changes: 63 additions & 0 deletions JM/CF/Scripts/3_Game/CommunityFramework/RPC/RPC_Context.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
class CF_RPC_Context extends ScriptRPC
{
protected string m_HandlerType;
protected string m_FunctionName;
protected bool m_Guaranteed;
protected Object m_Target;

Arkensor marked this conversation as resolved.
Show resolved Hide resolved
/**
* @brief Sends the prepared RPC context to the recipient machine. You can re-send the RPC to the same or a different recipient by calling SendTo() again.
* @code
* auto rpc = CF.RPC.Prepare("MyHandler", "MyFunction", true);
* rpc.SendTo(player.GetIdentity());
* rpc.SendTo(player2.GetIdentity());
* @endcode
*
*
* @param recipient PlayerIdentity of the receiving machine. When used on the client it can only target the server (NULL). NULL when sent from the server targets ALL players.
* @return bool Returns true if successfully registered, false otherwise (see script log for details).
*/
void SendTo(PlayerIdentity recipient = NULL)
{
Send(m_Target, CF_RPC.CF_RPC_SCRIPT_ID, m_Guaranteed, recipient);
}

/**
* @brief Resets the written data in the RPC context so it can be sent again with new data.
* @code
* auto rpc = CF.RPC.Prepare("MyHandler", "MyFunction", true);
* rpc.Write("My data");
* rpc.SendTo(...);
* rpc.ResetData();
* rpc.Write("My new data");
* rpc.SendTo(...);
* @endcode
*
* @return void
*/
void ResetData()
{
Reset();
Write(m_HandlerType);
Write(m_FunctionName);
}

/**
* @brief [Internal] Prepares a new RPC context. Used by CF.RPC.Prepare()
*
* @return CF_RPC_Context RPC write and send context.
*/
static ref CF_RPC_Context _Prepare(string handlerType, string functionName, bool guaranteed, Object target)
{
CF_RPC_Context ctx();
ctx.m_HandlerType = handlerType;
ctx.m_FunctionName = functionName;
ctx.m_Guaranteed = guaranteed;
ctx.m_Target = target;

ctx.Write(handlerType);
ctx.Write(functionName);

return ctx;
}
}
7 changes: 7 additions & 0 deletions JM/CF/Scripts/3_Game/CommunityFramework/RPC/RPC_HandlerBase.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class CF_RPC_HandlerBase
{
void CF_RPC_HandlerBase()
{
CF_RPC.RegisterHandler(this);
}
}