From 7b9fa972b6922e65cff81e9c94e4598e443a2039 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 27 Apr 2020 18:39:42 +0100 Subject: [PATCH 1/9] Add addGroup, removeGroup, groups, players API --- distribution/openrct2.d.ts | 9 +++-- src/openrct2/scripting/ScNetwork.hpp | 57 +++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index a3a9b858b2..62b1152b0d 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -562,13 +562,16 @@ declare global { */ interface Network { readonly mode: NetworkMode; - readonly groups: number; - readonly players: number; + readonly numGroups: number; + readonly numPlayers: number; + readonly groups: PlayerGroup[]; + readonly players: Player[]; defaultGroup: number; getServerInfo(): ServerInfo; + addGroup(): void; getGroup(index: number): PlayerGroup; - setGroups(groups: PlayerGroup[]): void; + removeGroup(index: number): void; getPlayer(index: number): Player; kickPlayer(index: number): void; sendMessage(message: string): void; diff --git a/src/openrct2/scripting/ScNetwork.hpp b/src/openrct2/scripting/ScNetwork.hpp index 94e5ab47a0..af0f6c7b3f 100644 --- a/src/openrct2/scripting/ScNetwork.hpp +++ b/src/openrct2/scripting/ScNetwork.hpp @@ -271,7 +271,7 @@ namespace OpenRCT2::Scripting # endif return "none"; } - int32_t players_get() const + int32_t numPlayers_get() const { # ifndef DISABLE_NETWORK return network_get_num_players(); @@ -279,7 +279,7 @@ namespace OpenRCT2::Scripting return 0; # endif } - int32_t groups_get() const + int32_t numGroups_get() const { # ifndef DISABLE_NETWORK return network_get_num_groups(); @@ -303,6 +303,34 @@ namespace OpenRCT2::Scripting # endif } + std::vector> groups_get() const + { + std::vector> groups; +# ifndef DISABLE_NETWORK + auto numGroups = network_get_num_groups(); + for (int32_t i = 0; i < numGroups; i++) + { + auto groupId = network_get_group_id(i); + groups.push_back(std::make_shared(groupId)); + } +# endif + return groups; + } + + std::vector> players_get() const + { + std::vector> players; +# ifndef DISABLE_NETWORK + auto numPlayers = network_get_num_players(); + for (int32_t i = 0; i < numPlayers; i++) + { + auto playerId = network_get_player_id(i); + players.push_back(std::make_shared(playerId)); + } +# endif + return players; + } + std::shared_ptr getPlayer(int32_t index) const { # ifndef DISABLE_NETWORK @@ -329,6 +357,27 @@ namespace OpenRCT2::Scripting return nullptr; } + void addGroup() + { +# ifndef DISABLE_NETWORK + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); + GameActions::Execute(&networkModifyGroup); +# endif + } + + void removeGroup(int32_t index) + { +# ifndef DISABLE_NETWORK + auto numGroups = network_get_num_groups(); + if (index < numGroups) + { + auto groupId = network_get_group_id(index); + auto networkAction = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, groupId); + GameActions::Execute(&networkAction); + } +# endif + } + void kickPlayer(int32_t index) { # ifndef DISABLE_NETWORK @@ -359,10 +408,14 @@ namespace OpenRCT2::Scripting static void Register(duk_context* ctx) { dukglue_register_property(ctx, &ScNetwork::mode_get, nullptr, "mode"); + dukglue_register_property(ctx, &ScNetwork::numGroups_get, nullptr, "numGroups"); + dukglue_register_property(ctx, &ScNetwork::numPlayers_get, nullptr, "numPlayers"); dukglue_register_property(ctx, &ScNetwork::groups_get, nullptr, "groups"); dukglue_register_property(ctx, &ScNetwork::players_get, nullptr, "players"); dukglue_register_property(ctx, &ScNetwork::defaultGroup_get, &ScNetwork::defaultGroup_set, "defaultGroup"); + dukglue_register_method(ctx, &ScNetwork::addGroup, "addGroup"); dukglue_register_method(ctx, &ScNetwork::getGroup, "getGroup"); + dukglue_register_method(ctx, &ScNetwork::removeGroup, "removeGroup"); dukglue_register_method(ctx, &ScNetwork::getPlayer, "getPlayer"); dukglue_register_method(ctx, &ScNetwork::kickPlayer, "kickPlayer"); dukglue_register_method(ctx, &ScNetwork::sendMessage, "sendMessage"); From ecce4da6e89bbe244094968ccd159a9def6e9bdb Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 27 Apr 2020 19:54:29 +0100 Subject: [PATCH 2/9] Add ipAddress and publicHashKey properties to player API --- distribution/openrct2.d.ts | 2 ++ src/openrct2/network/Network.cpp | 45 ++++++++++++++++++++++++++++ src/openrct2/network/Socket.cpp | 42 ++++++++++++++++++++++++-- src/openrct2/network/Socket.h | 1 + src/openrct2/network/network.h | 2 ++ src/openrct2/scripting/ScNetwork.hpp | 12 ++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 62b1152b0d..6fb51e0b1e 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -590,6 +590,8 @@ declare global { readonly ping: number; readonly commandsRan: number; readonly moneySpent: number; + readonly ipAddress: string; + readonly publicKeyHash: string; } interface PlayerGroup { diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index d1c011f7f2..087717b8a1 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -136,6 +136,7 @@ public: void ProcessDisconnectedClients(); std::vector>::iterator GetPlayerIteratorByID(uint8_t id); NetworkPlayer* GetPlayerByID(uint8_t id); + NetworkConnection* GetPlayerConnection(uint8_t id); std::vector>::iterator GetGroupIteratorByID(uint8_t id); NetworkGroup* GetGroupByID(uint8_t id); static const char* FormatChat(NetworkPlayer* fromplayer, const char* text); @@ -665,6 +666,22 @@ uint8_t Network::GetPlayerID() return player_id; } +NetworkConnection* Network::GetPlayerConnection(uint8_t id) +{ + auto player = GetPlayerByID(id); + if (player != nullptr) + { + for (auto& connection : client_connection_list) + { + if (connection->Player == player) + { + return connection.get(); + } + } + } + return nullptr; +} + void Network::Update() { _closeLock = true; @@ -3385,6 +3402,26 @@ money32 network_get_player_money_spent(uint32_t index) return gNetwork.player_list[index]->MoneySpent; } +std::string network_get_player_ip_address(uint32_t id) +{ + auto conn = gNetwork.GetPlayerConnection(id); + if (conn != nullptr) + { + return conn->Socket->GetIpAddress(); + } + return {}; +} + +std::string network_get_player_public_key_hash(uint32_t id) +{ + auto player = gNetwork.GetPlayerByID(id); + if (player != nullptr) + { + return player->KeyHash; + } + return {}; +} + void network_add_player_money_spent(uint32_t index, money32 cost) { gNetwork.player_list[index]->AddMoneySpent(cost); @@ -4054,6 +4091,14 @@ money32 network_get_player_money_spent(uint32_t index) { return MONEY(0, 0); } +std::string network_get_player_ip_address(uint32_t id) +{ + return {}; +} +std::string network_get_player_public_key_hash(uint32_t id) +{ + return {}; +} void network_add_player_money_spent(uint32_t index, money32 cost) { } diff --git a/src/openrct2/network/Socket.cpp b/src/openrct2/network/Socket.cpp index b4a7f0c08d..7c192084ef 100644 --- a/src/openrct2/network/Socket.cpp +++ b/src/openrct2/network/Socket.cpp @@ -201,6 +201,7 @@ private: uint16_t _listeningPort = 0; SOCKET _socket = INVALID_SOCKET; + std::string _ipAddress; std::string _hostName; std::future _connectFuture; std::string _error; @@ -322,18 +323,21 @@ public: } else { + auto ipAddress = GetIpAddressFromSocket(reinterpret_cast(&client_addr)); + char hostName[NI_MAXHOST]; int32_t rc = getnameinfo( reinterpret_cast(&client_addr), client_len, hostName, sizeof(hostName), nullptr, 0, NI_NUMERICHOST | NI_NUMERICSERV); SetOption(socket, IPPROTO_TCP, TCP_NODELAY, true); + if (rc == 0) { - tcpSocket = std::unique_ptr(new TcpSocket(socket, hostName)); + tcpSocket = std::unique_ptr(new TcpSocket(socket, hostName, ipAddress)); } else { - tcpSocket = std::unique_ptr(new TcpSocket(socket, "")); + tcpSocket = std::unique_ptr(new TcpSocket(socket, "", ipAddress)); } } } @@ -546,11 +550,17 @@ public: return _hostName.empty() ? nullptr : _hostName.c_str(); } + std::string GetIpAddress() const override + { + return _ipAddress; + } + private: - explicit TcpSocket(SOCKET socket, const std::string& hostName) + explicit TcpSocket(SOCKET socket, const std::string& hostName, const std::string& ipAddress) { _socket = socket; _hostName = hostName; + _ipAddress = ipAddress; _status = SOCKET_STATUS_CONNECTED; } @@ -563,6 +573,32 @@ private: } _status = SOCKET_STATUS_CLOSED; } + + std::string GetIpAddressFromSocket(const sockaddr_in* addr) + { + std::string result; +# if defined(__MINGW32__) + if (addr->sin_family == AF_INET) + { + result = inet_ntoa(addr->sin_addr); + } +# else + if (addr->sin_family == AF_INET) + { + char str[INET_ADDRSTRLEN]{}; + inet_ntop(AF_INET, &addr->sin_addr, str, sizeof(str)); + result = str; + } + else if (addr->sin_family == AF_INET6) + { + auto addrv6 = reinterpret_cast(&addr); + char str[INET6_ADDRSTRLEN]{}; + inet_ntop(AF_INET6, &addrv6->sin6_addr, str, sizeof(str)); + result = str; + } +# endif + return result; + } }; class UdpSocket final : public IUdpSocket, protected Socket diff --git a/src/openrct2/network/Socket.h b/src/openrct2/network/Socket.h index 601168b31b..562cad8d5f 100644 --- a/src/openrct2/network/Socket.h +++ b/src/openrct2/network/Socket.h @@ -55,6 +55,7 @@ public: virtual SOCKET_STATUS GetStatus() const abstract; virtual const char* GetError() const abstract; virtual const char* GetHostName() const abstract; + virtual std::string GetIpAddress() const abstract; virtual void Listen(uint16_t port) abstract; virtual void Listen(const std::string& address, uint16_t port) abstract; diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 1b3d079445..02e4ad3b7c 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -61,6 +61,8 @@ uint32_t network_get_player_flags(uint32_t index); int32_t network_get_player_ping(uint32_t index); int32_t network_get_player_id(uint32_t index); money32 network_get_player_money_spent(uint32_t index); +std::string network_get_player_ip_address(uint32_t id); +std::string network_get_player_public_key_hash(uint32_t id); void network_add_player_money_spent(uint32_t index, money32 cost); int32_t network_get_player_last_action(uint32_t index, int32_t time); void network_set_player_last_action(uint32_t index, int32_t command); diff --git a/src/openrct2/scripting/ScNetwork.hpp b/src/openrct2/scripting/ScNetwork.hpp index af0f6c7b3f..9e99ab4450 100644 --- a/src/openrct2/scripting/ScNetwork.hpp +++ b/src/openrct2/scripting/ScNetwork.hpp @@ -233,6 +233,16 @@ namespace OpenRCT2::Scripting # endif } + std::string ipAddress_get() const + { + return network_get_player_ip_address(_id); + } + + std::string publicKeyHash_get() const + { + return network_get_player_public_key_hash(_id); + } + static void Register(duk_context* ctx) { dukglue_register_property(ctx, &ScPlayer::id_get, nullptr, "id"); @@ -241,6 +251,8 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScPlayer::ping_get, nullptr, "ping"); dukglue_register_property(ctx, &ScPlayer::commandsRan_get, nullptr, "commandsRan"); dukglue_register_property(ctx, &ScPlayer::moneySpent_get, nullptr, "moneySpent"); + dukglue_register_property(ctx, &ScPlayer::ipAddress_get, nullptr, "ipAddress"); + dukglue_register_property(ctx, &ScPlayer::publicKeyHash_get, nullptr, "publicKeyHash"); } }; From 1f39ac014aaf180e125e2bd2ea3ded3961088568 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 28 Apr 2020 17:31:12 +0100 Subject: [PATCH 3/9] Run network.chat hook for server chat messages --- src/openrct2/network/Network.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 087717b8a1..c2231f6639 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -2903,7 +2903,7 @@ void Network::Client_Handle_CHAT([[maybe_unused]] NetworkConnection& connection, } } -static bool ProcessChatMessagePluginHooks(const NetworkPlayer& player, std::string& text) +static bool ProcessChatMessagePluginHooks(uint8_t playerId, std::string& text) { # ifdef ENABLE_SCRIPTING auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); @@ -2913,7 +2913,7 @@ static bool ProcessChatMessagePluginHooks(const NetworkPlayer& player, std::stri // Create event args object auto objIdx = duk_push_object(ctx); - duk_push_number(ctx, static_cast(player.Id)); + duk_push_number(ctx, playerId); duk_put_prop_string(ctx, objIdx, "player"); duk_push_string(ctx, text.c_str()); duk_put_prop_string(ctx, objIdx, "message"); @@ -2957,7 +2957,7 @@ void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& p std::string text = szText; if (connection.Player != nullptr) { - if (!ProcessChatMessagePluginHooks(*connection.Player, text)) + if (!ProcessChatMessagePluginHooks(connection.Player->Id, text)) { // Message not to be relayed return; @@ -3875,10 +3875,17 @@ void network_send_chat(const char* text) } else if (gNetwork.GetMode() == NETWORK_MODE_SERVER) { - NetworkPlayer* player = gNetwork.GetPlayerByID(gNetwork.GetPlayerID()); - const char* formatted = gNetwork.FormatChat(player, text); - chat_history_add(formatted); - gNetwork.Server_Send_CHAT(formatted); + std::string message = text; + if (ProcessChatMessagePluginHooks(gNetwork.GetPlayerID(), message)) + { + auto player = gNetwork.GetPlayerByID(gNetwork.GetPlayerID()); + if (player != nullptr) + { + auto formatted = gNetwork.FormatChat(player, message.c_str()); + chat_history_add(formatted); + gNetwork.Server_Send_CHAT(formatted); + } + } } } From d0154d08e950755273c77b0b5ff884a90836f755 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 28 Apr 2020 19:33:30 +0100 Subject: [PATCH 4/9] Implement network join / leave hooks --- distribution/openrct2.d.ts | 8 ++ src/openrct2/network/Network.cpp | 129 +++++++++++++++++++++++--- src/openrct2/scripting/Duktape.hpp | 5 + src/openrct2/scripting/HookEngine.cpp | 3 + src/openrct2/scripting/HookEngine.h | 3 + 5 files changed, 136 insertions(+), 12 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 6fb51e0b1e..599e8262da 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -175,6 +175,7 @@ declare global { subscribe(hook: "interval.tick", callback: () => void): IDisposable; subscribe(hook: "interval.day", callback: () => void): IDisposable; subscribe(hook: "network.chat", callback: (e: NetworkChatEventArgs) => void): IDisposable; + subscribe(hook: "network.authenticate", callback: (e: NetworkAuthenticateEventArgs) => void): IDisposable; subscribe(hook: "network.join", callback: (e: NetworkEventArgs) => void): IDisposable; subscribe(hook: "network.leave", callback: (e: NetworkEventArgs) => void): IDisposable; } @@ -257,6 +258,13 @@ declare global { message: string; } + interface NetworkAuthenticateEventArgs { + readonly name: number; + readonly ipAddress: string; + readonly publicKeyHash: string; + cancel: boolean; + } + /** * APIs for the in-game date. */ diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index c2231f6639..017fee9b83 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -1959,6 +1959,80 @@ void Network::ProcessPending() ProcessPlayerList(); } +static bool ProcessPlayerAuthenticatePluginHooks( + const NetworkConnection& connection, const std::string_view& name, const std::string_view& publicKeyHash) +{ +# ifdef ENABLE_SCRIPTING + using namespace OpenRCT2::Scripting; + + auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); + if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_AUTHENTICATE)) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + + // Create event args object + DukObject eObj(ctx); + eObj.Set("name", name); + eObj.Set("publicKeyHash", publicKeyHash); + eObj.Set("ipAddress", connection.Socket->GetIpAddress()); + eObj.Set("cancel", false); + auto e = eObj.Take(); + + // Call the subscriptions + hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_AUTHENTICATE, e, false); + + // Check if any hook has cancelled the join + if (AsOrDefault(e["cancel"], false)) + { + return false; + } + } +# endif + return true; +} + +static void ProcessPlayerJoinedPluginHooks(uint8_t playerId) +{ +# ifdef ENABLE_SCRIPTING + using namespace OpenRCT2::Scripting; + + auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); + if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_JOIN)) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + + // Create event args object + DukObject eObj(ctx); + eObj.Set("player", playerId); + auto e = eObj.Take(); + + // Call the subscriptions + hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_JOIN, e, false); + } +# endif +} + +static void ProcessPlayerLeftPluginHooks(uint8_t playerId) +{ +# ifdef ENABLE_SCRIPTING + using namespace OpenRCT2::Scripting; + + auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); + if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_LEAVE)) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + + // Create event args object + DukObject eObj(ctx); + eObj.Set("player", playerId); + auto e = eObj.Take(); + + // Call the subscriptions + hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_LEAVE, e, false); + } +# endif +} + void Network::ProcessPlayerList() { if (GetMode() == NETWORK_MODE_SERVER) @@ -1983,6 +2057,8 @@ void Network::ProcessPlayerList() // List of active players found in the list. std::vector activePlayerIds; + std::vector newPlayers; + std::vector removedPlayers; for (auto&& pendingPlayer : itPending->second.players) { @@ -2001,6 +2077,8 @@ void Network::ProcessPlayerList() _serverConnection->Player = player; } } + + newPlayers.push_back(player->Id); } else { @@ -2010,19 +2088,35 @@ void Network::ProcessPlayerList() } // Remove any players that are not in newly received list - auto it = player_list.begin(); - while (it != player_list.end()) + for (const auto& player : player_list) { - if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end()) + if (std::find(activePlayerIds.begin(), activePlayerIds.end(), player->Id) == activePlayerIds.end()) { - it = player_list.erase(it); - } - else - { - it++; + removedPlayers.push_back(player->Id); } } + // Run player removed hooks (must be before players removed from list) + for (auto playerId : removedPlayers) + { + ProcessPlayerLeftPluginHooks(playerId); + } + + // Run player joined hooks (must be after players added to list) + for (auto playerId : newPlayers) + { + ProcessPlayerJoinedPluginHooks(playerId); + } + + // Now actually remove removed players from player list + player_list.erase( + std::remove_if( + player_list.begin(), player_list.end(), + [&removedPlayers](const std::unique_ptr& player) { + return std::find(removedPlayers.begin(), removedPlayers.end(), player->Id) != removedPlayers.end(); + }), + player_list.end()); + _pendingPlayerLists.erase(itPending); itPending = _pendingPlayerLists.begin(); } @@ -2117,6 +2211,8 @@ void Network::ServerClientDisconnected(std::unique_ptr& conne // Log player disconnected event AppendServerLog(text); + + ProcessPlayerLeftPluginHooks(connection_player->Id); } void Network::RemovePlayer(std::unique_ptr& connection) @@ -2384,9 +2480,9 @@ void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p void Network::Server_Client_Joined(const char* name, const std::string& keyhash, NetworkConnection& connection) { - NetworkPlayer* player = AddPlayer(name, keyhash); + auto player = AddPlayer(name, keyhash); connection.Player = player; - if (player) + if (player != nullptr) { char text[256]; const char* player_name = static_cast(player->Name.c_str()); @@ -2404,6 +2500,8 @@ void Network::Server_Client_Joined(const char* name, const std::string& keyhash, player_name = static_cast(playerNameHash.c_str()); format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name); AppendServerLog(text); + + ProcessPlayerJoinedPluginHooks(player->Id); } } @@ -2685,9 +2783,16 @@ void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p } else if (connection.AuthStatus == NETWORK_AUTH_VERIFIED) { - connection.AuthStatus = NETWORK_AUTH_OK; const std::string hash = connection.Key.PublicKeyHash(); - Server_Client_Joined(name, hash, connection); + if (ProcessPlayerAuthenticatePluginHooks(connection, name, hash)) + { + connection.AuthStatus = NETWORK_AUTH_OK; + Server_Client_Joined(name, hash, connection); + } + else + { + connection.AuthStatus = NETWORK_AUTH_VERIFICATIONFAILURE; + } } else if (connection.AuthStatus != NETWORK_AUTH_REQUIREPASSWORD) { diff --git a/src/openrct2/scripting/Duktape.hpp b/src/openrct2/scripting/Duktape.hpp index 6091b0a2ca..61c9c4be0b 100644 --- a/src/openrct2/scripting/Duktape.hpp +++ b/src/openrct2/scripting/Duktape.hpp @@ -37,6 +37,11 @@ namespace OpenRCT2::Scripting return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue; } + template<> inline bool AsOrDefault(const DukValue& value, const bool& defaultValue) + { + return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue; + } + /** * Allows creation of an object on the duktape stack and setting properties on it before * retrieving the DukValue instance of it. diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index 20010f4dc7..bfed9d0b76 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -25,6 +25,9 @@ HOOK_TYPE OpenRCT2::Scripting::GetHookType(const std::string& name) { "interval.tick", HOOK_TYPE::INTERVAL_TICK }, { "interval.day", HOOK_TYPE::INTERVAL_DAY }, { "network.chat", HOOK_TYPE::NETWORK_CHAT }, + { "network.authenticate", HOOK_TYPE::NETWORK_AUTHENTICATE }, + { "network.join", HOOK_TYPE::NETWORK_JOIN }, + { "network.leave", HOOK_TYPE::NETWORK_LEAVE }, }); auto result = LookupTable.find(name); return (result != LookupTable.end()) ? result->second : HOOK_TYPE::UNDEFINED; diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index 8971ad9d60..896cbe7292 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -33,6 +33,9 @@ namespace OpenRCT2::Scripting INTERVAL_TICK, INTERVAL_DAY, NETWORK_CHAT, + NETWORK_AUTHENTICATE, + NETWORK_JOIN, + NETWORK_LEAVE, COUNT, UNDEFINED = -1, }; From 297fe537b6b35ea6b8474beae37b2f2cbda9c1fa Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 28 Apr 2020 22:02:14 +0100 Subject: [PATCH 5/9] Implement network.sendMessage(msg, players) --- src/openrct2/network/Network.cpp | 33 ++++++++++++++++++++++------ src/openrct2/network/network.h | 3 ++- src/openrct2/scripting/ScNetwork.hpp | 21 +++++++++++++++++- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 017fee9b83..eaef82f509 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -177,7 +177,7 @@ public: void Server_Send_TOKEN(NetworkConnection& connection); void Server_Send_MAP(NetworkConnection* connection = nullptr); void Client_Send_CHAT(const char* text); - void Server_Send_CHAT(const char* text); + void Server_Send_CHAT(const char* text, const std::vector& playerIds = {}); void Client_Send_GAME_ACTION(const GameAction* action); void Server_Send_GAME_ACTION(const GameAction* action); void Server_Send_TICK(); @@ -1671,12 +1671,27 @@ void Network::Client_Send_CHAT(const char* text) _serverConnection->QueuePacket(std::move(packet)); } -void Network::Server_Send_CHAT(const char* text) +void Network::Server_Send_CHAT(const char* text, const std::vector& playerIds) { std::unique_ptr packet(NetworkPacket::Allocate()); *packet << static_cast(NETWORK_COMMAND_CHAT); packet->WriteString(text); - SendPacketToClients(*packet); + + if (playerIds.empty()) + { + SendPacketToClients(*packet); + } + else + { + for (auto playerId : playerIds) + { + auto conn = GetPlayerConnection(playerId); + if (conn != nullptr && !conn->IsDisconnected) + { + conn->QueuePacket(NetworkPacket::Duplicate(*packet)); + } + } + } } void Network::Client_Send_GAME_ACTION(const GameAction* action) @@ -3972,7 +3987,7 @@ void network_send_map() gNetwork.Server_Send_MAP(); } -void network_send_chat(const char* text) +void network_send_chat(const char* text, const std::vector& playerIds) { if (gNetwork.GetMode() == NETWORK_MODE_CLIENT) { @@ -3987,8 +4002,12 @@ void network_send_chat(const char* text) if (player != nullptr) { auto formatted = gNetwork.FormatChat(player, message.c_str()); - chat_history_add(formatted); - gNetwork.Server_Send_CHAT(formatted); + if (playerIds.empty() + || std::find(playerIds.begin(), playerIds.end(), gNetwork.GetPlayerID()) != playerIds.end()) + { + chat_history_add(formatted); + } + gNetwork.Server_Send_CHAT(formatted, playerIds); } } } @@ -4311,7 +4330,7 @@ int32_t network_get_pickup_peep_old_x(uint8_t playerid) { return _pickup_peep_old_x; } -void network_send_chat(const char* text) +void network_send_chat(const char* text, const std::vector& playerIds) { } void network_send_password(const std::string& password) diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 02e4ad3b7c..380b01a2ed 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -20,6 +20,7 @@ #include #include +#include struct json_t; struct GameAction; @@ -94,7 +95,7 @@ void network_set_pickup_peep_old_x(uint8_t playerid, int32_t x); int32_t network_get_pickup_peep_old_x(uint8_t playerid); void network_send_map(); -void network_send_chat(const char* text); +void network_send_chat(const char* text, const std::vector& playerIds = {}); void network_send_game_action(const GameAction* action); void network_enqueue_game_action(const GameAction* action); void network_send_password(const std::string& password); diff --git a/src/openrct2/scripting/ScNetwork.hpp b/src/openrct2/scripting/ScNetwork.hpp index 9e99ab4450..5cb76da3f1 100644 --- a/src/openrct2/scripting/ScNetwork.hpp +++ b/src/openrct2/scripting/ScNetwork.hpp @@ -408,7 +408,26 @@ namespace OpenRCT2::Scripting # ifndef DISABLE_NETWORK if (players.is_array()) { - duk_error(players.context(), DUK_ERR_ERROR, "Not yet supported"); + if (network_get_mode() == NETWORK_MODE_SERVER) + { + std::vector playerIds; + auto playerArray = players.as_array(); + for (const auto& item : playerArray) + { + if (item.type() == DukValue::Type::NUMBER) + { + playerIds.push_back(static_cast(item.as_int())); + } + } + if (!playerArray.empty()) + { + network_send_chat(message.c_str(), playerIds); + } + } + else + { + duk_error(players.context(), DUK_ERR_ERROR, "Only servers can send private messages."); + } } else { From f642597098c1e4f4d6e8b68aa8d4dc208fc49187 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 30 Apr 2020 01:54:12 +0100 Subject: [PATCH 6/9] Replace loop with std::find_if --- src/openrct2/network/Network.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index eaef82f509..96c760be7a 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -671,13 +671,10 @@ NetworkConnection* Network::GetPlayerConnection(uint8_t id) auto player = GetPlayerByID(id); if (player != nullptr) { - for (auto& connection : client_connection_list) - { - if (connection->Player == player) - { - return connection.get(); - } - } + return std::find_if( + client_connection_list.begin(), client_connection_list.end(), + [player](const auto& conn) -> bool { return conn->Player == player; }) + ->get(); } return nullptr; } From 7897df140d5db183f28ae537be61aeabf6221eb1 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 30 Apr 2020 02:14:33 +0100 Subject: [PATCH 7/9] Update src/openrct2/network/Network.cpp Co-Authored-By: Tulio Leao --- src/openrct2/network/Network.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 96c760be7a..71d5bb836d 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -671,10 +671,10 @@ NetworkConnection* Network::GetPlayerConnection(uint8_t id) auto player = GetPlayerByID(id); if (player != nullptr) { - return std::find_if( + auto clientIt = std::find_if( client_connection_list.begin(), client_connection_list.end(), - [player](const auto& conn) -> bool { return conn->Player == player; }) - ->get(); + [player](const auto& conn) -> bool { return conn->Player == player; }); + return clientIt != client_connection_list.end() ? clientIt->get() : nullptr; } return nullptr; } From ee8a1a86c1350653bfb7edc34fca423cd1837af0 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 30 Apr 2020 02:18:32 +0100 Subject: [PATCH 8/9] Add comments and null check --- src/openrct2/network/Network.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 71d5bb836d..6a005b94bd 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -1676,6 +1676,7 @@ void Network::Server_Send_CHAT(const char* text, const std::vector& pla if (playerIds.empty()) { + // Empty players / default value means send to all players SendPacketToClients(*packet); } else @@ -3522,7 +3523,7 @@ money32 network_get_player_money_spent(uint32_t index) std::string network_get_player_ip_address(uint32_t id) { auto conn = gNetwork.GetPlayerConnection(id); - if (conn != nullptr) + if (conn != nullptr && conn->Socket != nullptr) { return conn->Socket->GetIpAddress(); } @@ -4002,6 +4003,7 @@ void network_send_chat(const char* text, const std::vector& playerIds) if (playerIds.empty() || std::find(playerIds.begin(), playerIds.end(), gNetwork.GetPlayerID()) != playerIds.end()) { + // Server is one of the recipients chat_history_add(formatted); } gNetwork.Server_Send_CHAT(formatted, playerIds); From 3d2a534d3b6dfa3a9df99f66273a4f00348892c2 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 30 Apr 2020 17:12:02 +0100 Subject: [PATCH 9/9] Fix formatting --- src/openrct2/network/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 6a005b94bd..ed3a7d6c38 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -672,8 +672,8 @@ NetworkConnection* Network::GetPlayerConnection(uint8_t id) if (player != nullptr) { auto clientIt = std::find_if( - client_connection_list.begin(), client_connection_list.end(), - [player](const auto& conn) -> bool { return conn->Player == player; }); + client_connection_list.begin(), client_connection_list.end(), + [player](const auto& conn) -> bool { return conn->Player == player; }); return clientIt != client_connection_list.end() ? clientIt->get() : nullptr; } return nullptr;