From c8fbc2e529b0fe34421a4144f74fab3037c02bbf Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 10 Feb 2020 23:33:00 +0000 Subject: [PATCH] Improve chat subscription --- distribution/openrct2.d.ts | 8 ++++- src/openrct2/network/Network.cpp | 50 +++++++++++++++++++++------ src/openrct2/scripting/HookEngine.cpp | 29 ++++++++++++++++ src/openrct2/scripting/HookEngine.h | 5 ++- 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 57de82f7e0..9f0d27e701 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -43,6 +43,11 @@ export type HookType = "interval.tick" | "interval.day" | "network.chat" | "network.action" | "network.join" | "network.leave"; +export interface NetworkChatEventArgs { + player: number; + message: string; +} + export interface Context { /** * The user's current configuration. @@ -68,6 +73,7 @@ export interface Context { * Subscribes to the given hook. */ subscribe(hook: HookType, callback: Function): IDisposable; + subscribe(hook: "network.chat", callback: (e: NetworkChatEventArgs) => void): IDisposable; } export interface IntentDesc @@ -401,7 +407,7 @@ export interface Network { getGroup(index: number): PlayerGroup; setGroups(groups: PlayerGroup[]): void; getPlayer(index: number): Player; - kickPlayer(index: number): Player; + kickPlayer(index: number): void; sendMessage(message: string): void; sendMessage(message: string, players: number[]): void; } diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index e0fc690a72..1a5724e5cd 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -2822,6 +2822,10 @@ void Network::Client_Handle_CHAT([[maybe_unused]] NetworkConnection& connection, void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& packet) { + auto szText = packet.ReadString(); + if (szText == nullptr || szText[0] == '\0') + return; + if (connection.Player) { NetworkGroup* group = GetGroupByID(connection.Player->Group); @@ -2830,19 +2834,44 @@ void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& p return; } } - const char* text = packet.ReadString(); - if (text) + + std::string text = szText; + if (connection.Player != nullptr) { auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); - hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_CHAT, { - { "player", (int32_t)connection.Player->Id }, - { "message", std::string(text) } - }); + if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_CHAT)) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); - const char* formatted = FormatChat(connection.Player, text); - chat_history_add(formatted); - Server_Send_CHAT(formatted); + // Create event args object + auto objIdx = duk_push_object(ctx); + duk_push_number(ctx, (int32_t)connection.Player->Id); + duk_put_prop_string(ctx, objIdx, "player"); + duk_push_string(ctx, text.c_str()); + duk_put_prop_string(ctx, objIdx, "message"); + auto e = DukValue::take_from_stack(ctx); + + // Call the subscriptions + hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_CHAT, e); + + // Update text from object if subscriptions changed it + if (e["message"].type() != DukValue::Type::STRING) + { + // Subscription set text to non-string, do not relay message + return; + } + text = e["message"].as_string(); + if (text.empty()) + { + // Subscription set text to empty string, do not relay message + return; + } + } } + + const char* formatted = FormatChat(connection.Player, text.c_str()); + chat_history_add(formatted); + Server_Send_CHAT(formatted); } void Network::Client_Handle_GAME_ACTION([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet) @@ -3582,7 +3611,8 @@ GameActionResult::Ptr network_kick_player(NetworkPlayerId_t playerId, bool isExe NetworkPlayer* player = gNetwork.GetPlayerByID(playerId); if (player == nullptr) { - // Player might be already removed by the PLAYERLIST command, need to refactor non-game commands executing too early. + // Player might be already removed by the PLAYERLIST command, need to refactor non-game commands executing too + // early. return std::make_unique(GA_ERROR::UNKNOWN, STR_NONE); } diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index d87c7d4669..695586b9cb 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -87,6 +87,12 @@ void HookEngine::UnsubscribeAll() } } +bool HookEngine::HasSubscriptions(HOOK_TYPE type) const +{ + auto& hookList = GetHookList(type); + return !hookList.Hooks.empty(); +} + void HookEngine::Call(HOOK_TYPE type) { auto& hookList = GetHookList(type); @@ -101,6 +107,23 @@ void HookEngine::Call(HOOK_TYPE type) } } +void HookEngine::Call(HOOK_TYPE type, DukValue args) +{ + auto& hookList = GetHookList(type); + for (auto& hook : hookList.Hooks) + { + ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner); + + const auto& function = hook.Function; + auto ctx = function.context(); + function.push(); + + args.push(); + duk_pcall(ctx, 1); + duk_pop(ctx); + } +} + void HookEngine::Call(HOOK_TYPE type, const std::initializer_list>& args) { auto& hookList = GetHookList(type); @@ -141,3 +164,9 @@ HookList& HookEngine::GetHookList(HOOK_TYPE type) auto index = static_cast(type); return _hookMap[index]; } + +const HookList& HookEngine::GetHookList(HOOK_TYPE type) const +{ + auto index = static_cast(type); + return _hookMap[index]; +} diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index 7c417e64c5..e2dde251b8 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -51,7 +51,7 @@ namespace OpenRCT2::Scripting struct HookList { - HOOK_TYPE Type; + HOOK_TYPE Type{}; std::vector Hooks; HookList() @@ -80,10 +80,13 @@ namespace OpenRCT2::Scripting void Unsubscribe(HOOK_TYPE type, uint32_t cookie); void UnsubscribeAll(std::shared_ptr owner); void UnsubscribeAll(); + bool HasSubscriptions(HOOK_TYPE type) const; void Call(HOOK_TYPE type); + void Call(HOOK_TYPE type, DukValue args); void Call(HOOK_TYPE type, const std::initializer_list>& args); private: HookList& GetHookList(HOOK_TYPE type); + const HookList& GetHookList(HOOK_TYPE type) const; }; } // namespace OpenRCT2::Scripting