From 530dfac7c9ccd0ec18f1e1006d62741e42393049 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 2 May 2023 15:25:05 +0100 Subject: [PATCH] Fix #20104: [Plugin] Some network APIs use player index and group index (#20115) Change all APIs that took a player index or group index so that they now take unique IDs. --- distribution/changelog.txt | 1 + distribution/openrct2.d.ts | 154 ++++++++++++++++-- distribution/scripting.md | 1 + src/openrct2/scripting/ScriptEngine.h | 3 +- .../scripting/bindings/network/ScNetwork.cpp | 95 ++++++++--- .../scripting/bindings/network/ScNetwork.hpp | 8 +- 6 files changed, 219 insertions(+), 43 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 95a7ecaffc..249c5d7f71 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -42,6 +42,7 @@ - Fix: [#20016] The group box for small scenery details in the Tile Inspector window has unused empty space. - Fix: [#20018] Shops not calculating up-keep cost. - Fix: [#20033] Asset packs cannot reference game data. +- Fix: [#20104] [Plugin] Some network APIs use player index and group index. 0.4.4 (2023-03-28) ------------------------------------------------------------------------ diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index bb79a1785a..fd02debea6 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -2886,25 +2886,100 @@ declare global { * Use `network.mode` to determine whether the current game is a client, server or in single player mode. */ interface Network { + /** + * The current network mode. This can be used to determine whether the current + * session is single player, a multiplayer server, or a multiplayer client. + */ readonly mode: NetworkMode; + + /** + * The number of multiplayer groups there are in the server. + */ readonly numGroups: number; + + /** + * The number of players there are in the server. + */ readonly numPlayers: number; + + /** + * Gets all the multiplayer groups within the server. Groups are used to give individual + * players roles and permissions. + */ readonly groups: PlayerGroup[]; + + /** + * Gets all the players that are currently in the server. + */ readonly players: Player[]; + + /** + * The player this instance of the game is controlling. + */ readonly currentPlayer: Player; + + /** + * Gets or sets the default group ID that new players joining the server should be assigned to. + */ defaultGroup: number; + + /** + * Various statistics related to networking. + */ readonly stats: NetworkStats; - getServerInfo(): ServerInfo; + /** + * Creates a new multiplayer group for managing player permissions. + */ addGroup(): void; - getGroup(index: number): PlayerGroup; - removeGroup(index: number): void; - getPlayer(index: number): Player; - kickPlayer(index: number): void; + + /** + * Gets the player group with the specified ID. + * @param id The group ID. Prior to API version 77, this is the group index. + */ + getGroup(id: number): PlayerGroup; + + /** + * Removes the player group with the specified ID. + * @param id The group ID. Prior to API version 77, this is the group index. + */ + removeGroup(id: number): void; + + /* + * Gets the player with the specified ID. + * @param id The player ID. Prior to API version 77, this is the player index. + */ + getPlayer(id: number): Player; + + /* + * Kicks the player with the specified ID from the server. + * @param id The player ID. Prior to API version 77, this is the player index. + */ + kickPlayer(id: number): void; + + /** + * Sends a chat message to all players. + * @param message The message text. + */ sendMessage(message: string): void; + + /** + * Sends a chat message to only the specified players. + * @param message The message text. + * @param players A list of player IDs that should receive the chat message. + * Note: the message will be internally transmitted to players via + * the server, even if the server is not a recipient. + */ sendMessage(message: string, players: number[]): void; + /** + * Creates a new listener that can accept TCP connections on a given port. + */ createListener(): Listener; + + /** + * Creates a new TCP client that can connect to a server. + */ createSocket(): Socket; } @@ -2914,34 +2989,81 @@ declare global { * Represents a player within a network game. */ interface Player { + /** + * The unique ID for the player. + */ readonly id: number; + + /** + * The name of the player. + */ readonly name: string; + + /** + * The group ID the player is a member of. + */ group: number; + + /** + * The latest measured ping in milliseconds for the player. + */ readonly ping: number; + + /** + * The number of actions the player has successfully executed. + */ readonly commandsRan: number; + + /** + * The total amount of cash spent from actions performed by the player. + */ readonly moneySpent: number; + + /** + * The player's IP address. + */ readonly ipAddress: string; + + /** + * A hash of the player's public key used to authenticate with the server. + */ readonly publicKeyHash: string; } + /** + * Represents a group in a network game for assigning roles and permissions + * to one or more players. + */ interface PlayerGroup { + /** + * The unique ID for the group. + */ readonly id: number; + + /** + * The name of the group. + */ name: string; + + /** + * The permissions granted to each player belonging to the group. + */ permissions: PermissionType[]; } - interface ServerInfo { - readonly name: string; - readonly description: string; - readonly greeting: string; - readonly providerName: string; - readonly providerEmail: string; - readonly providerWebsite: string; - } - + /** + * Represents various network statistics. + */ interface NetworkStats { - bytesReceived: number[]; - bytesSent: number[]; + /** + * The number of bytes received for each category. + */ + readonly bytesReceived: number[]; + + /** + * The number of bytes sent for each category. + */ + readonly bytesSent: number[]; } type PermissionType = diff --git a/distribution/scripting.md b/distribution/scripting.md index a78ebc81f6..201b049d2e 100644 --- a/distribution/scripting.md +++ b/distribution/scripting.md @@ -59,6 +59,7 @@ As of version 34 there are breaking Api changes. - **Version 34:** `Entity.type` will now return `"guest"` or `"staff"` instead of `"peep"`. - **Version 63:** Accessing G2 sprites by id directly is now deprecated in favor of a future-proof implementation using `IconName` and/or `context.getIcon()`. - **Version 68:** Custom game actions registered through `context.registerAction()` now wrap the callback arguments in a `GameActionEventArgs`, similar to `context.subscribe()` callbacks. +- **Version 77:** Network APIs that take player index and group index now take player ID and group ID instead. ## Frequently Asked Questions diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 113c6b834a..ac870caa85 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -47,12 +47,13 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 76; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 77; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; static constexpr int32_t API_VERSION_63_G2_REORDER = 63; static constexpr int32_t API_VERSION_68_CUSTOM_ACTION_ARGS = 68; + static constexpr int32_t API_VERSION_77_NETWORK_IDS = 77; # ifndef DISABLE_NETWORK class ScSocketBase; diff --git a/src/openrct2/scripting/bindings/network/ScNetwork.cpp b/src/openrct2/scripting/bindings/network/ScNetwork.cpp index 9a2ce39ff7..9343504aa9 100644 --- a/src/openrct2/scripting/bindings/network/ScNetwork.cpp +++ b/src/openrct2/scripting/bindings/network/ScNetwork.cpp @@ -111,15 +111,28 @@ namespace OpenRCT2::Scripting return player; } - std::shared_ptr ScNetwork::getPlayer(int32_t index) const + std::shared_ptr ScNetwork::getPlayer(int32_t id) const { # ifndef DISABLE_NETWORK - auto numPlayers = NetworkGetNumPlayers(); - if (index < numPlayers) + if (GetTargetAPIVersion() < API_VERSION_77_NETWORK_IDS) { - auto playerId = NetworkGetPlayerID(index); - return std::make_shared(playerId); + auto index = id; + auto numPlayers = NetworkGetNumPlayers(); + if (index < numPlayers) + { + auto playerId = NetworkGetPlayerID(index); + return std::make_shared(playerId); + } } + else + { + auto index = NetworkGetPlayerIndex(id); + if (index != -1) + { + return std::make_shared(id); + } + } + # endif return nullptr; } @@ -157,14 +170,26 @@ namespace OpenRCT2::Scripting # endif } - std::shared_ptr ScNetwork::getGroup(int32_t index) const + std::shared_ptr ScNetwork::getGroup(int32_t id) const { # ifndef DISABLE_NETWORK - auto numGroups = NetworkGetNumGroups(); - if (index < numGroups) + if (GetTargetAPIVersion() < API_VERSION_77_NETWORK_IDS) { - auto groupId = NetworkGetGroupID(index); - return std::make_shared(groupId); + auto index = id; + auto numGroups = NetworkGetNumGroups(); + if (index < numGroups) + { + auto groupId = NetworkGetGroupID(index); + return std::make_shared(groupId); + } + } + else + { + auto index = NetworkGetGroupIndex(id); + if (index != -1) + { + return std::make_shared(id); + } } # endif return nullptr; @@ -178,28 +203,54 @@ namespace OpenRCT2::Scripting # endif } - void ScNetwork::removeGroup(int32_t index) + void ScNetwork::removeGroup(int32_t id) { # ifndef DISABLE_NETWORK - auto numGroups = NetworkGetNumGroups(); - if (index < numGroups) + if (GetTargetAPIVersion() < API_VERSION_77_NETWORK_IDS) { - auto groupId = NetworkGetGroupID(index); - auto networkAction = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, groupId); - GameActions::Execute(&networkAction); + auto index = id; + auto numGroups = NetworkGetNumGroups(); + if (index < numGroups) + { + auto groupId = NetworkGetGroupID(index); + auto networkAction = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, groupId); + GameActions::Execute(&networkAction); + } + } + else + { + auto index = NetworkGetGroupIndex(id); + if (index != -1) + { + auto networkAction = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, id); + GameActions::Execute(&networkAction); + } } # endif } - void ScNetwork::kickPlayer(int32_t index) + void ScNetwork::kickPlayer(int32_t id) { # ifndef DISABLE_NETWORK - auto numPlayers = NetworkGetNumPlayers(); - if (index < numPlayers) + if (GetTargetAPIVersion() < API_VERSION_77_NETWORK_IDS) { - auto playerId = NetworkGetPlayerID(index); - auto kickPlayerAction = PlayerKickAction(playerId); - GameActions::Execute(&kickPlayerAction); + auto index = id; + auto numPlayers = NetworkGetNumPlayers(); + if (index < numPlayers) + { + auto playerId = NetworkGetPlayerID(index); + auto kickPlayerAction = PlayerKickAction(playerId); + GameActions::Execute(&kickPlayerAction); + } + } + else + { + auto index = NetworkGetPlayerIndex(id); + if (index != -1) + { + auto kickPlayerAction = PlayerKickAction(id); + GameActions::Execute(&kickPlayerAction); + } } # endif } diff --git a/src/openrct2/scripting/bindings/network/ScNetwork.hpp b/src/openrct2/scripting/bindings/network/ScNetwork.hpp index b0a6e00dba..827041a0c2 100644 --- a/src/openrct2/scripting/bindings/network/ScNetwork.hpp +++ b/src/openrct2/scripting/bindings/network/ScNetwork.hpp @@ -43,17 +43,17 @@ namespace OpenRCT2::Scripting std::shared_ptr currentPlayer_get() const; - std::shared_ptr getPlayer(int32_t index) const; + std::shared_ptr getPlayer(int32_t id) const; DukValue stats_get() const; - std::shared_ptr getGroup(int32_t index) const; + std::shared_ptr getGroup(int32_t id) const; void addGroup(); - void removeGroup(int32_t index); + void removeGroup(int32_t id); - void kickPlayer(int32_t index); + void kickPlayer(int32_t id); void sendMessage(std::string message, DukValue players);