Skip to content

Commit

Permalink
feat(scripting/net): Unreliable network events
Browse files Browse the repository at this point in the history
This introduces a new API to send unreliable client and server events from within all ScRTs.
  • Loading branch information
tens0rfl0w committed Oct 5, 2024
1 parent 0bf3d3e commit 47bf4da
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 47 deletions.
38 changes: 38 additions & 0 deletions code/client/clrcore-v2/Interop/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,36 @@ public static void TriggerEvent(CString eventName, params object[] args)
public static void TriggerServerEvent(string eventName, params object[] args)
=> CoreNatives.TriggerServerEventInternal(eventName, args);

public static void TriggerUnreliableServerEvent(string eventName, params object[] args)
=> CoreNatives.TriggerUnreliableServerEventInternal(eventName, args);

public static void TriggerLatentServerEvent(string eventName, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerLatentServerEventInternal(eventName, args, bytesPerSecond);

public static void TriggerServerEvent(CString eventName, params object[] args)
=> CoreNatives.TriggerServerEventInternal(eventName, args);

public static void TriggerUnreliableServerEvent(CString eventName, params object[] args)
=> CoreNatives.TriggerUnreliableServerEventInternal(eventName, args);

public static void TriggerLatentServerEvent(CString eventName, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerLatentServerEventInternal(eventName, args, bytesPerSecond);
#else
public static void TriggerClientEvent(string eventName, Shared.Player player, params object[] args)
=> CoreNatives.TriggerClientEventInternal(eventName, player.m_handle, args);

public static void TriggerUnreliableClientEvent(string eventName, Shared.Player player, params object[] args)
=> CoreNatives.TriggerUnreliableClientEventInternal(eventName, player.m_handle, args);

public static void TriggerLatentClientEvent(string eventName, Shared.Player player, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerLatentClientEventInternal(eventName, player.m_handle, args, bytesPerSecond);

public static void TriggerClientEvent(CString eventName, Shared.Player player, params object[] args)
=> CoreNatives.TriggerClientEventInternal(eventName, player.m_handle, args);

public static void TriggerUnreliableClientEvent(CString eventName, Shared.Player player, params object[] args)
=> CoreNatives.TriggerUnreliableClientEventInternal(eventName, player.m_handle, args);

public static void TriggerLatentClientEvent(CString eventName, Shared.Player player, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerLatentClientEventInternal(eventName, player.m_handle, args, bytesPerSecond);

Expand All @@ -87,6 +99,14 @@ public static void TriggerLatentClientEvent(CString eventName, Shared.Player pla
public static void TriggerAllClientsEvent(string eventName, params object[] args)
=> CoreNatives.TriggerClientEventInternal(eventName, AllPlayers, args);

/// <summary>
/// Broadcasts an unreliable event to all connected players.
/// </summary>
/// <param name="eventName">The name of the event.</param>
/// <param name="args">Arguments to pass to the event.</param>
public static void TriggerUnreliableAllClientsEvent(string eventName, params object[] args)
=> CoreNatives.TriggerUnreliableClientEventInternal(eventName, AllPlayers, args);

/// <summary>
/// Broadcasts an event to all connected players.
/// </summary>
Expand All @@ -103,12 +123,18 @@ public static void TriggerLatentAllClientsEvent(string eventName, int bytesPerSe
public static void TriggerServerEvent(string eventName, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);

public static void TriggerUnreliableServerEvent(string eventName, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);

public static void TriggerLatentServerEvent(string eventName, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);

public static void TriggerServerEvent(CString eventName, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);

public static void TriggerUnreliableServerEvent(CString eventName, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);

public static void TriggerLatentServerEvent(CString eventName, int bytesPerSecond, params object[] args)
=> CoreNatives.TriggerEventInternal(eventName, args);
#else
Expand All @@ -117,6 +143,10 @@ public static void TriggerLatentServerEvent(CString eventName, int bytesPerSecon
public static void TriggerClientEvent(string eventName, Shared.Player player, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerUnreliableClientEvent(string eventName, Shared.Player player, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerLatentClientEvent(string eventName, Shared.Player player, int bytesPerSecond, params object[] args)
=> throw new NotSupportedException();
Expand All @@ -125,6 +155,10 @@ public static void TriggerLatentClientEvent(string eventName, Shared.Player play
public static void TriggerClientEvent(CString eventName, Shared.Player player, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerUnreliableClientEvent(CString eventName, Shared.Player player, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerLatentClientEvent(CString eventName, Shared.Player player, int bytesPerSecond, params object[] args)
=> throw new NotSupportedException();
Expand All @@ -133,6 +167,10 @@ public static void TriggerLatentClientEvent(CString eventName, Shared.Player pla
public static void TriggerAllClientsEvent(string eventName, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerUnreliableAllClientsEvent(string eventName, params object[] args)
=> throw new NotSupportedException();

[EditorBrowsable(EditorBrowsableState.Never)]
public static void TriggerLatentAllClientsEvent(string eventName, int bytesPerSecond, params object[] args)
=> throw new NotSupportedException();
Expand Down
22 changes: 22 additions & 0 deletions code/client/clrcore-v2/Native/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ internal static class CoreNatives
private static UIntPtr s_unregisterRawNuiCallback; // 0x7fb46432
#if IS_FXSERVER
private static UIntPtr s_0x2f7a49e6;
private static UIntPtr s_triggerUnreliableClientEventInternal; // 0x8d3913f4
private static UIntPtr s_0x70b35890;
#else
private static UIntPtr s_0x7fdd1128;
private static UIntPtr s_triggerUnreliableServerEventInternal; // 0x6c7a0f4d
private static UIntPtr s_0x128737ea;
#endif

Expand Down Expand Up @@ -141,6 +143,16 @@ internal unsafe static void TriggerClientEventInternal(CString eventName, CStrin
}
}

[SecuritySafeCritical]
internal unsafe static void TriggerUnreliableClientEventInternal(CString eventName, CString target, InPacket args)
{
fixed (byte* p_eventName = eventName?.value, p_target = target?.value, p_args = args.value)
{
ulong* __data = stackalloc ulong[] { (ulong)p_eventName, (ulong)p_target, (ulong)p_args, (ulong)args.value?.LongLength };
ScriptContext.InvokeNative(ref s_triggerUnreliableClientEventInternal, 0x8d3913f4, __data, 4); // TRIGGER_UNRELIABLE_CLIENT_EVENT_INTERNAL
}
}

[SecuritySafeCritical]
internal unsafe static void TriggerLatentClientEventInternal(CString eventName, CString target, InPacket args, int bytesPerSecond)
{
Expand All @@ -161,6 +173,16 @@ internal unsafe static void TriggerServerEventInternal(CString eventName, InPack
}
}

[SecuritySafeCritical]
internal unsafe static void TriggerUnreliableServerEventInternal(CString eventName, InPacket args)
{
fixed (byte* p_eventName = eventName?.value, p_args = args.value)
{
ulong* __data = stackalloc ulong[] { (ulong)p_eventName, (ulong)p_args, (ulong)args.value?.LongLength };
ScriptContext.InvokeNative(ref s_triggerUnreliableServerEventInternal, 0x6c7a0f4d, __data, 3); // TRIGGER_UNRELIABLE_SERVER_EVENT_INTERNAL
}
}

[SecuritySafeCritical]
internal unsafe static void TriggerLatentServerEventInternal(CString eventName, InPacket args, int bytesPerSecond)
{
Expand Down
45 changes: 45 additions & 0 deletions code/client/clrcore/BaseScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ public static void TriggerServerEvent(string eventName, params object[] args)
TriggerEventInternal(eventName, argsSerialized, true);
}

[SecuritySafeCritical]
public static void TriggerUnreliableServerEvent(string eventName, params object[] args)
{
var argsSerialized = MsgPackSerializer.Serialize(args);

TriggerUnreliableServerEventInternal(eventName, argsSerialized);
}

[SecuritySafeCritical]
public static void TriggerLatentServerEvent(string eventName, int bytesPerSecond, params object[] args)
{
Expand Down Expand Up @@ -261,6 +269,29 @@ public static void TriggerClientEvent(string eventName, params object[] args)
}
}

public static void TriggerUnreliableClientEvent(Player player, string eventName, params object[] args)
{
player.TriggerUnreliableEvent(eventName, args);
}

/// <summary>
/// Broadcasts an unreliable event to all connected players.
/// </summary>
/// <param name="eventName">The name of the event.</param>
/// <param name="args">Arguments to pass to the event.</param>
public static void TriggerUnreliableClientEvent(string eventName, params object[] args)
{
var argsSerialized = MsgPackSerializer.Serialize(args);

unsafe
{
fixed (byte* serialized = &argsSerialized[0])
{
Function.Call(Hash.TRIGGER_UNRELIABLE_CLIENT_EVENT_INTERNAL, eventName, "-1", serialized, argsSerialized.Length);
}
}
}

public static void TriggerLatentClientEvent(Player player, string eventName, int bytesPerSecond, params object[] args)
{
player.TriggerLatentEvent(eventName, bytesPerSecond, args);
Expand All @@ -286,6 +317,20 @@ public static void TriggerLatentClientEvent(string eventName, int bytesPerSecond
#endif

#if !IS_FXSERVER
[SecurityCritical]
private static void TriggerUnreliableServerEventInternal(string eventName, byte[] argsSerialized)
{
var nativeHash = Hash.TRIGGER_UNRELIABLE_SERVER_EVENT_INTERNAL;

unsafe
{
fixed (byte* serialized = &argsSerialized[0])
{
Function.Call(nativeHash, eventName, serialized, argsSerialized.Length);
}
}
}

[SecurityCritical]
private static void TriggerLatentServerEventInternal(string eventName, byte[] argsSerialized, int bytesPerSecond)
{
Expand Down
17 changes: 17 additions & 0 deletions code/client/clrcore/Server/ServerWrappers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ public void TriggerEvent(string eventName, params object[] args)
#endif
}

public void TriggerUnreliableEvent(string eventName, params object[] args)
{
#if MONO_V2
CoreNatives.TriggerUnreliableClientEventInternal(eventName, m_handle, args);
#else
var argsSerialized = MsgPackSerializer.Serialize(args);

unsafe
{
fixed (byte* serialized = &argsSerialized[0])
{
Function.Call(Hash.TRIGGER_UNRELIABLE_CLIENT_EVENT_INTERNAL, eventName, m_handle, serialized, argsSerialized.Length);
}
}
#endif
}

public void TriggerLatentEvent(string eventName, int bytesPerSecond, params object[] args)
{
#if MONO_V2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,25 @@ namespace fx
ConVar<bool> g_enableEventReassembly("sv_enableNetEventReassembly", ConVar_Replicated, true, EnableEventReassemblyChanged);
}

namespace
{
void TriggerServerEventInternal(fx::ScriptContext& context, bool reliable)
{
std::string_view eventName = context.GetArgument<const char*>(0);
size_t payloadSize = context.GetArgument<uint32_t>(2);

std::string_view eventPayload = std::string_view(context.GetArgument<const char*>(1), payloadSize);

g_netLibrary->OnTriggerServerEvent(eventName, eventPayload);

net::packet::ClientServerEventPacket clientServerEventPacket;
// null terminated event name
clientServerEventPacket.data.eventName = { reinterpret_cast<uint8_t*>(const_cast<char*>(context.GetArgument<const char*>(0))), eventName.size() + 1 };
clientServerEventPacket.data.eventData = { reinterpret_cast<uint8_t*>(const_cast<char*>(eventPayload.data())), eventPayload.size() };
g_netLibrary->SendNetPacket(clientServerEventPacket, reliable);
}
}

void NetLibraryResourcesComponent::UpdateOneResource()
{
if (!m_resourceUpdateQueue.empty())
Expand Down Expand Up @@ -729,20 +748,14 @@ void NetLibraryResourcesComponent::AttachToObject(NetLibrary* netLibrary)
}
});

fx::ScriptEngine::RegisterNativeHandler("TRIGGER_SERVER_EVENT_INTERNAL", [netLibrary](fx::ScriptContext& context)
fx::ScriptEngine::RegisterNativeHandler("TRIGGER_UNRELIABLE_SERVER_EVENT_INTERNAL", [](fx::ScriptContext& context)
{
std::string_view eventName = context.GetArgument<const char*>(0);
size_t payloadSize = context.GetArgument<uint32_t>(2);

std::string_view eventPayload = std::string_view(context.GetArgument<const char*>(1), payloadSize);

netLibrary->OnTriggerServerEvent(eventName, eventPayload);
TriggerServerEventInternal(context, false);
});

net::packet::ClientServerEventPacket clientServerEventPacket;
// null terminated event name
clientServerEventPacket.data.eventName = {reinterpret_cast<uint8_t*>(const_cast<char*>(context.GetArgument<const char*>(0))), eventName.size() + 1};
clientServerEventPacket.data.eventData = {reinterpret_cast<uint8_t*>(const_cast<char*>(eventPayload.data())), eventPayload.size()};
netLibrary->SendNetPacket(clientServerEventPacket);
fx::ScriptEngine::RegisterNativeHandler("TRIGGER_SERVER_EVENT_INTERNAL", [](fx::ScriptContext& context)
{
TriggerServerEventInternal(context, true);
});

fx::ScriptEngine::RegisterNativeHandler("REQUEST_RESOURCE_FILE_SET", [this](fx::ScriptContext& context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace fx
class ServerEventComponent : public fwRefCountable, public IAttached<ServerInstanceBase>
{
public:
virtual void TriggerClientEvent(const std::string_view& eventName, const void* data, size_t dataLen, const std::optional<std::string_view>& targetSrc = std::optional<std::string_view>());
virtual void TriggerClientEvent(const std::string_view& eventName, const void* data, size_t dataLen, const std::optional<std::string_view>& targetSrc = std::optional<std::string_view>(), bool reliable = true);

inline virtual void AttachToObject(ServerInstanceBase* object) override
{
Expand Down
58 changes: 34 additions & 24 deletions code/components/citizen-server-impl/src/ServerResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,32 @@ namespace

EnableEventReassemblyChangedWithInstance(variableEntry, instance, rac);
}

void TriggerClientEventInternal(fx::ScriptContext& context, bool reliable)
{
std::string_view eventName = context.CheckArgument<const char*>(0);
std::optional<std::string_view> targetSrc;

{
auto targetSrcIdx = context.CheckArgument<const char*>(1);

if (strcmp(targetSrcIdx, "-1") != 0)
{
targetSrc = targetSrcIdx;
}
}

const void* data = context.GetArgument<const void*>(2);
uint32_t dataLen = context.GetArgument<uint32_t>(3);

// get the current resource manager
auto resourceManager = fx::ResourceManager::GetCurrent();

// get the owning server instance
auto instance = resourceManager->GetComponent<fx::ServerInstanceBaseRef>()->Get();

instance->GetComponent<fx::ServerEventComponent>()->TriggerClientEvent(eventName, data, dataLen, targetSrc, reliable);
}
}

static void ScanResources(fx::ServerInstanceBase* instance)
Expand Down Expand Up @@ -826,7 +852,7 @@ static InitFunction initFunction([]()
#include <ScriptEngine.h>
#include <optional>

void fx::ServerEventComponent::TriggerClientEvent(const std::string_view& eventName, const void* data, size_t dataLen, const std::optional<std::string_view>& targetSrc)
void fx::ServerEventComponent::TriggerClientEvent(const std::string_view& eventName, const void* data, size_t dataLen, const std::optional<std::string_view>& targetSrc, const bool reliable)
{
// build the target event
net::Buffer outBuffer;
Expand Down Expand Up @@ -860,14 +886,14 @@ void fx::ServerEventComponent::TriggerClientEvent(const std::string_view& eventN
fx::WarningDeprecationf<ScriptDeprecations::CLIENT_EVENT_OLD_NET_ID>("natives", "TRIGGER_CLIENT_EVENT_INTERNAL: client %d is not the same as the target %d. This happens when the oldId from the playerJoining event is used. Use source instead.\n", client->GetNetId(), targetNetId);
}
// TODO(fxserver): >MTU size?
client->SendPacket(0, outBuffer, NetPacketType_Reliable);
client->SendPacket(0, outBuffer, reliable ? NetPacketType_Reliable : NetPacketType_Unreliable);
}
}
else
{
clientRegistry->ForAllClients([&](const fx::ClientSharedPtr& client)
{
client->SendPacket(0, outBuffer, NetPacketType_Reliable);
client->SendPacket(0, outBuffer, reliable ? NetPacketType_Reliable : NetPacketType_Unreliable);
});
}
}
Expand Down Expand Up @@ -913,28 +939,12 @@ static InitFunction initFunction2([]()

fx::ScriptEngine::RegisterNativeHandler("TRIGGER_CLIENT_EVENT_INTERNAL", [](fx::ScriptContext& context)
{
std::string_view eventName = context.CheckArgument<const char*>(0);
std::optional<std::string_view> targetSrc;

{
auto targetSrcIdx = context.CheckArgument<const char*>(1);

if (strcmp(targetSrcIdx, "-1") != 0)
{
targetSrc = targetSrcIdx;
}
}

const void* data = context.GetArgument<const void*>(2);
uint32_t dataLen = context.GetArgument<uint32_t>(3);

// get the current resource manager
auto resourceManager = fx::ResourceManager::GetCurrent();

// get the owning server instance
auto instance = resourceManager->GetComponent<fx::ServerInstanceBaseRef>()->Get();
TriggerClientEventInternal(context, true);
});

instance->GetComponent<fx::ServerEventComponent>()->TriggerClientEvent(eventName, data, dataLen, targetSrc);
fx::ScriptEngine::RegisterNativeHandler("TRIGGER_UNRELIABLE_CLIENT_EVENT_INTERNAL", [](fx::ScriptContext& context)
{
TriggerClientEventInternal(context, false);
});

fx::ScriptEngine::RegisterNativeHandler("TRIGGER_LATENT_CLIENT_EVENT_INTERNAL", TriggerDisabledLatentClientEventInternal);
Expand Down
Loading

0 comments on commit 47bf4da

Please sign in to comment.