Skip to content

Commit

Permalink
Add server -> client RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
xLuxy committed Sep 30, 2023
1 parent eda208f commit ec8db97
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 31 deletions.
68 changes: 52 additions & 16 deletions client/src/CV8Resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ bool CV8ResourceImpl::Stop()
modules.clear();
requiresMap.clear();

remoteHandlers.clear();
rpcHandlers.clear();

isPreloading = true;

V8ResourceImpl::Stop();
Expand All @@ -237,11 +240,15 @@ void CV8ResourceImpl::OnEvent(const alt::CEvent* e)
v8::Context::Scope scope(GetContext());

#ifdef ALT_CLIENT_API
if (e->GetType() == alt::CEvent::Type::SERVER_SCRIPT_RPC_ANSWER_EVENT)
if (e->GetType() == alt::CEvent::Type::SCRIPT_RPC_ANSWER_EVENT)
{
auto ev = static_cast<const alt::CServerScriptRPCAnswerEvent*>(e);
auto ev = static_cast<const alt::CScriptRPCAnswerEvent*>(e);
HandleRPCAnswer(ev);
}
else if (e->GetType() == alt::CEvent::Type::SCRIPT_RPC_EVENT)
{
HandleServerRPC((alt::CScriptRPCEvent*)e);
}
#endif

V8Helpers::EventHandler* handler = V8Helpers::EventHandler::Get(e);
Expand Down Expand Up @@ -300,38 +307,67 @@ void CV8ResourceImpl::OnEvent(const alt::CEvent* e)
}
}

void CV8ResourceImpl::HandleRPCAnswer(const alt::CServerScriptRPCAnswerEvent* ev)
void CV8ResourceImpl::HandleRPCAnswer(const alt::CScriptRPCAnswerEvent* ev)
{
auto answerId = ev->GetAnswerID();

auto it = rpcHandlers.find(answerId);
auto it = remoteRPCHandlers.find(answerId);

if (it == rpcHandlers.end())
if (it == remoteRPCHandlers.end())
return;

auto context = GetContext();
auto isolate = GetIsolate();
auto resource = Get(isolate->GetEnteredOrMicrotaskContext());

auto promise = it->second.Get(isolate);
if (auto resource = Get(isolate->GetEnteredOrMicrotaskContext()); !resource->GetResource()->IsStarted())
return;

if (!promise->IsPromise())
if (auto promise = it->second.Get(isolate); promise->IsPromise())
{
rpcHandlers.erase(it);
return;
if (auto errorMessage = ev->GetAnswerError(); !errorMessage.empty())
promise->Reject(context, V8Helpers::JSValue(errorMessage));
else
promise->Resolve(context, V8Helpers::MValueToV8(ev->GetAnswer()));
}

if (!resource->GetResource()->IsStarted())
remoteRPCHandlers.erase(it);
}

void CV8ResourceImpl::HandleServerRPC(alt::CScriptRPCEvent* ev)
{
auto handler = rpcHandlers.find(ev->GetName());

if (handler == rpcHandlers.end())
return;

if (auto errorMessage = ev->GetAnswerError(); !errorMessage.empty())
auto context = GetContext();
auto isolate = GetIsolate();

std::vector<v8::Local<v8::Value>> args;
V8Helpers::MValueArgsToV8(ev->GetArgs(), args);

v8::TryCatch tryCatch(isolate);
auto result = V8Helpers::CallFunctionWithTimeout(handler->second.Get(isolate), context, args);

alt::MValue returnValue;

if (!result.IsEmpty())
returnValue = V8Helpers::V8ToMValue(result.ToLocalChecked());
else
returnValue = V8Helpers::V8ToMValue(v8::Undefined(isolate));

ev->WillAnswer();

std::string errorMessage;
if (tryCatch.HasCaught())
{
promise->Reject(context, V8Helpers::JSValue(errorMessage));
return;
errorMessage = "Unknown error";

if (!tryCatch.Message().IsEmpty())
errorMessage = *v8::String::Utf8Value(isolate, tryCatch.Message()->Get());
}

promise->Resolve(context, V8Helpers::MValueToV8(ev->GetAnswer()));
rpcHandlers.erase(it);
alt::ICore::Instance().TriggerServerRPCAnswer(ev->GetAnswerID(), returnValue, errorMessage);
}

std::vector<V8Helpers::EventCallback*> CV8ResourceImpl::GetWebViewHandlers(alt::IWebView* view, const std::string& name)
Expand Down
3 changes: 2 additions & 1 deletion client/src/CV8Resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class CV8ResourceImpl : public V8ResourceImpl, public IImportHandler
}
}

void HandleRPCAnswer(const alt::CServerScriptRPCAnswerEvent* ev);
void HandleRPCAnswer(const alt::CScriptRPCAnswerEvent* ev);
void HandleServerRPC(alt::CScriptRPCEvent* ev);
std::vector<V8Helpers::EventCallback*> GetWebViewHandlers(alt::IWebView* view, const std::string& name);

void SubscribeWebSocketClient(alt::IWebSocketClient* webSocket, const std::string& evName, v8::Local<v8::Function> cb, V8Helpers::SourceLocation&& location)
Expand Down
40 changes: 39 additions & 1 deletion client/src/bindings/ClientBindingsMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,42 @@ static void EmitServer(const v8::FunctionCallbackInfo<v8::Value>& info)
alt::ICore::Instance().TriggerServerEvent(eventName, args);
}

static void OnRpc(const v8::FunctionCallbackInfo<v8::Value>& info)
{
V8_GET_ISOLATE_CONTEXT();
V8_CHECK_ARGS_LEN(2);

V8_ARG_TO_STRING(1, rpcName);
V8_ARG_TO_FUNCTION(2, callback);

V8_CHECK(!V8ResourceImpl::rpcHandlers.contains(rpcName), "Handler already registered");

V8ResourceImpl::rpcHandlers[rpcName] = v8::Global<v8::Function>(isolate, callback);
}

static void OffRpc(const v8::FunctionCallbackInfo<v8::Value>& info)
{
V8_GET_ISOLATE_CONTEXT_RESOURCE();
V8_CHECK_ARGS_LEN_MIN_MAX(1, 2);

V8_ARG_TO_STRING(1, rpcName);

if (V8ResourceImpl::rpcHandlers.contains(rpcName))
{
if (info[1]->IsFunction())
{
V8_ARG_TO_FUNCTION(2, callback);

if(V8ResourceImpl::rpcHandlers[rpcName].Get(isolate)->StrictEquals(callback))
V8ResourceImpl::rpcHandlers.erase(rpcName);

return;
}

V8ResourceImpl::rpcHandlers.erase(rpcName);
}
}

static void EmitRPC(const v8::FunctionCallbackInfo<v8::Value>& info)
{
V8_GET_ISOLATE_CONTEXT_RESOURCE();
Expand All @@ -97,7 +133,7 @@ static void EmitRPC(const v8::FunctionCallbackInfo<v8::Value>& info)
auto answerId = alt::ICore::Instance().TriggerServerRPCEvent(rpcName, args);
auto persistent = V8Helpers::CPersistent<v8::Promise::Resolver>(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked());

V8ResourceImpl::rpcHandlers[answerId] = persistent;
V8ResourceImpl::remoteRPCHandlers[answerId] = persistent;
V8_RETURN(persistent.Get(isolate)->GetPromise());
}

Expand Down Expand Up @@ -1228,6 +1264,8 @@ extern V8Module altModule("alt",
V8Helpers::RegisterFunc(exports, "offServer", &OffServer);
V8Helpers::RegisterFunc(exports, "emitServer", &EmitServer);

V8Helpers::RegisterFunc(exports, "onRpc", &OnRpc);
V8Helpers::RegisterFunc(exports, "offRpc", &OffRpc);
V8Helpers::RegisterFunc(exports, "emitRpc", &EmitRPC);

V8Helpers::RegisterFunc(exports, "emitServerRaw", &EmitServerRaw);
Expand Down
51 changes: 48 additions & 3 deletions server/src/CNodeResourceImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ bool CNodeResourceImpl::Stop()
delete uvLoop;

vehiclePassengers.clear();

rpcHandlers.clear();
remoteRPCHandlers.clear();

return true;
}
Expand Down Expand Up @@ -145,9 +147,21 @@ void CNodeResourceImpl::OnEvent(const alt::CEvent* e)

HandleVehiclePassengerSeatEvents(e);

if (e->GetType() == alt::CEvent::Type::CLIENT_SCRIPT_RPC_EVENT)
if (e->GetType() == alt::CEvent::Type::SCRIPT_RPC_EVENT)
{
HandleClientRpcEvent((alt::CScriptRPCEvent*)e);
}
else if (e->GetType() == alt::CEvent::Type::SCRIPT_RPC_ANSWER_EVENT)
{
HandleClientRpcEvent((alt::CClientScriptRPCEvent*)e);
auto ev = static_cast<const alt::CScriptRPCAnswerEvent*>(e);
HandleClientRpcAnswerEvent(ev);
}
else if (e->GetType() == alt::CEvent::Type::DISCONNECT_EVENT)
{
auto ev = static_cast<const alt::CPlayerDisconnectEvent*>(e);
auto player = ev->GetTarget();

remoteRPCHandlers.erase(player);
}

V8Helpers::EventHandler* handler = V8Helpers::EventHandler::Get(e);
Expand Down Expand Up @@ -245,7 +259,7 @@ void CNodeResourceImpl::HandleVehiclePassengerSeatEvents(const alt::CEvent* ev)
}
}

void CNodeResourceImpl::HandleClientRpcEvent(alt::CClientScriptRPCEvent* ev)
void CNodeResourceImpl::HandleClientRpcEvent(alt::CScriptRPCEvent* ev)
{
auto handler = rpcHandlers.find(ev->GetName());

Expand Down Expand Up @@ -283,6 +297,37 @@ void CNodeResourceImpl::HandleClientRpcEvent(alt::CClientScriptRPCEvent* ev)
alt::ICore::Instance().TriggerClientRPCAnswer(ev->GetTarget(), ev->GetAnswerID(), returnValue, errorMessage);
}

void CNodeResourceImpl::HandleClientRpcAnswerEvent(const alt::CScriptRPCAnswerEvent* ev)
{
auto context = GetContext();
auto isolate = GetIsolate();

if (auto resource = Get(isolate->GetEnteredOrMicrotaskContext()); !resource->GetResource()->IsStarted())
return;

auto player = ev->GetTarget();
auto answerId = ev->GetAnswerID();

for (auto it = remoteRPCHandlers[player].begin(); it != remoteRPCHandlers[player].end(); ++it)
{
if (it->AnswerId != answerId)
continue;

if (auto promise = it->PromiseResolver.Get(isolate); promise->IsPromise())
{
if (auto errorMessage = ev->GetAnswerError(); !errorMessage.empty())
promise->Reject(context, V8Helpers::JSValue(errorMessage));
else
promise->Resolve(context, V8Helpers::MValueToV8(ev->GetAnswer()));
}

remoteRPCHandlers[player].erase(it);

if (remoteRPCHandlers[player].empty())
remoteRPCHandlers.erase(player);
}
}

void CNodeResourceImpl::OnTick()
{
v8::Locker locker(isolate);
Expand Down
3 changes: 2 additions & 1 deletion server/src/CNodeResourceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class CNodeResourceImpl : public V8ResourceImpl

void OnEvent(const alt::CEvent* ev) override;
void HandleVehiclePassengerSeatEvents(const alt::CEvent* ev);
void HandleClientRpcEvent(alt::CClientScriptRPCEvent* ev);
void HandleClientRpcEvent(alt::CScriptRPCEvent* ev);
void HandleClientRpcAnswerEvent(const alt::CScriptRPCAnswerEvent* ev);

void OnTick() override;

Expand Down
1 change: 0 additions & 1 deletion server/src/bindings/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ static void OffRpc(const v8::FunctionCallbackInfo<v8::Value>& info)

V8ResourceImpl::rpcHandlers.erase(rpcName);
}

}

static void SetSyncedMeta(const v8::FunctionCallbackInfo<v8::Value>& info)
Expand Down
21 changes: 21 additions & 0 deletions server/src/bindings/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,25 @@ static void EmitRaw(const v8::FunctionCallbackInfo<v8::Value>& info)
alt::ICore::Instance().TriggerClientEvent(player, eventName, mvArgs);
}

static void EmitRPC(const v8::FunctionCallbackInfo<v8::Value>& info)
{
V8_GET_ISOLATE_CONTEXT();
V8_CHECK_ARGS_LEN_MIN(1);
V8_GET_THIS_BASE_OBJECT(player, alt::IPlayer);

V8_ARG_TO_STRING(1, rpcName);

alt::MValueArgs args;
for(int i = 1; i < info.Length(); ++i)
args.emplace_back(V8Helpers::V8ToMValue(info[i], false));

auto answerId = alt::ICore::Instance().TriggerClientRPCEvent(player, rpcName, args);
auto persistent = V8Helpers::CPersistent<v8::Promise::Resolver>(isolate, v8::Promise::Resolver::New(ctx).ToLocalChecked());

V8ResourceImpl::remoteRPCHandlers[player].push_back({ answerId, persistent });
V8_RETURN(persistent.Get(isolate)->GetPromise());
}

static void HasLocalMeta(const v8::FunctionCallbackInfo<v8::Value>& info)
{
V8_GET_ISOLATE_CONTEXT();
Expand Down Expand Up @@ -1401,6 +1420,8 @@ extern V8Class v8Player("Player",
V8Helpers::SetMethod(isolate, tpl, "emit", &Emit);
V8Helpers::SetMethod(isolate, tpl, "emitRaw", &EmitRaw);

V8Helpers::SetMethod(isolate, tpl, "emitRpc", &EmitRPC);

V8Helpers::SetMethod(isolate, tpl, "hasLocalMeta", &HasLocalMeta);
V8Helpers::SetMethod(isolate, tpl, "setLocalMeta", &SetLocalMeta);
V8Helpers::SetMethod(isolate, tpl, "getLocalMeta", &GetLocalMeta);
Expand Down
12 changes: 6 additions & 6 deletions shared/IRuntimeEventHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ class IRuntimeEventHandler
using EventType = alt::CEvent::Type;
// All events the module uses, which need to be enabled and never disabled
static constexpr std::array internalEvents = {
EventType::CONNECTION_COMPLETE, EventType::DISCONNECT_EVENT, EventType::GAME_ENTITY_CREATE, EventType::GAME_ENTITY_DESTROY, EventType::RESOURCE_STOP
EventType::CONNECTION_COMPLETE, EventType::DISCONNECT_EVENT, EventType::GAME_ENTITY_CREATE, EventType::GAME_ENTITY_DESTROY, EventType::RESOURCE_STOP,
EventType::DISCONNECT_EVENT,

// RPC events
EventType::SCRIPT_RPC_EVENT, EventType::SCRIPT_RPC_ANSWER_EVENT

#ifdef ALT_SERVER_API
// required for vehicle seat stuff
, EventType::PLAYER_ENTER_VEHICLE, EventType::PLAYER_LEAVE_VEHICLE, EventType::PLAYER_CHANGE_VEHICLE_SEAT,

// RPC events
EventType::CLIENT_SCRIPT_RPC_EVENT
#else
, EventType::SERVER_SCRIPT_RPC_ANSWER_EVENT
#endif
};

Expand Down
10 changes: 9 additions & 1 deletion shared/V8ResourceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,18 @@ class V8ResourceImpl : public alt::IResource::Impl
// Vehicle passengers
static inline std::unordered_map<alt::IVehicle*, std::unordered_map<uint8_t, alt::IPlayer*>> vehiclePassengers{};

struct RemoteRPCHandler
{
uint16_t AnswerId;
V8Helpers::CPersistent<v8::Promise::Resolver> PromiseResolver;
};

// rpcs
static inline std::unordered_map<std::string, v8::Global<v8::Function>> rpcHandlers{};
static inline std::unordered_map<alt::IPlayer*, std::vector<RemoteRPCHandler>> remoteRPCHandlers{};
#else
static inline std::unordered_map<uint16_t, V8Helpers::CPersistent<v8::Promise::Resolver>> rpcHandlers{};
static inline std::unordered_map<uint16_t, V8Helpers::CPersistent<v8::Promise::Resolver>> remoteRPCHandlers{};
static inline std::unordered_map<std::string, v8::Global<v8::Function>> rpcHandlers{};
#endif

protected:
Expand Down
2 changes: 1 addition & 1 deletion shared/deps/cpp-sdk

0 comments on commit ec8db97

Please sign in to comment.