diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index b9612a607a..d4c56644cc 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -464,6 +464,11 @@ static void WindowMultiplayerInformationPaint(rct_window* w, rct_drawpixelinfo* #pragma region Players page +static bool IsServerPlayerInvisible() +{ + return network_is_server_player_invisible() && !gConfigGeneral.debugging_tools; +} + static void WindowMultiplayerPlayersMouseup(rct_window* w, WidgetIndex widgetIndex) { switch (widgetIndex) @@ -487,7 +492,7 @@ static void WindowMultiplayerPlayersResize(rct_window* w) { window_set_resize(*w, 420, 124, 500, 450); - w->no_list_items = network_get_num_players(); + w->no_list_items = (IsServerPlayerInvisible() ? network_get_num_visible_players() : network_get_num_players()); w->list_item_positions[0] = 0; w->widgets[WIDX_HEADER_PING].right = w->width - 5; @@ -534,7 +539,8 @@ static void WindowMultiplayerPlayersScrollmousedown(rct_window* w, int32_t scrol w->selected_list_item = index; w->Invalidate(); - WindowPlayerOpen(network_get_player_id(index)); + int32_t player = (IsServerPlayerInvisible() ? index + 1 : index); + WindowPlayerOpen(network_get_player_id(player)); } static void WindowMultiplayerPlayersScrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) @@ -577,7 +583,11 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo { ScreenCoordsXY screenCoords; screenCoords.y = 0; - for (int32_t i = 0; i < network_get_num_players(); i++) + + const int32_t firstPlayerInList = (IsServerPlayerInvisible() ? 1 : 0); + int32_t listPosition = 0; + + for (int32_t player = firstPlayerInList; player < network_get_num_players(); player++) { if (screenCoords.y > dpi->y + dpi->height) { @@ -592,17 +602,17 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo // Draw player name colour_t colour = COLOUR_BLACK; - if (i == w->selected_list_item) + if (listPosition == w->selected_list_item) { gfx_filter_rect( dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); - _buffer += network_get_player_name(i); + _buffer += network_get_player_name(player); colour = w->colours[2]; } else { - if (network_get_player_flags(i) & NETWORK_PLAYER_FLAG_ISSERVER) + if (network_get_player_flags(player) & NETWORK_PLAYER_FLAG_ISSERVER) { _buffer += "{BABYBLUE}"; } @@ -610,7 +620,7 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo { _buffer += "{BLACK}"; } - _buffer += network_get_player_name(i); + _buffer += network_get_player_name(player); } screenCoords.x = 0; gfx_clip_string(_buffer.data(), 230, FontSpriteBase::MEDIUM); @@ -618,7 +628,7 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo // Draw group name _buffer.resize(0); - int32_t group = network_get_group_index(network_get_player_group(i)); + int32_t group = network_get_group_index(network_get_player_group(player)); if (group != -1) { _buffer += "{BLACK}"; @@ -629,7 +639,7 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo } // Draw last action - int32_t action = network_get_player_last_action(i, 2000); + int32_t action = network_get_player_last_action(player, 2000); auto ft = Formatter(); if (action != -999) { @@ -643,7 +653,7 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo // Draw ping _buffer.resize(0); - int32_t ping = network_get_player_ping(i); + int32_t ping = network_get_player_ping(player); if (ping <= 100) { _buffer += "{GREEN}"; @@ -665,6 +675,7 @@ static void WindowMultiplayerPlayersScrollpaint(rct_window* w, rct_drawpixelinfo gfx_draw_string(dpi, screenCoords, _buffer.c_str(), { colour }); } screenCoords.y += SCROLLABLE_ROW_HEIGHT; + listPosition++; } } diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 40ca5db9b6..3fd2df2236 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1003,7 +1003,7 @@ static void WindowTopToolbarPaint(rct_window* w, rct_drawpixelinfo* dpi) // Draw number of players. auto ft = Formatter(); - ft.Add(network_get_num_players()); + ft.Add(network_get_num_visible_players()); DrawTextBasic( dpi, screenPos + ScreenCoordsXY{ 23, 1 }, STR_COMMA16, ft, { COLOUR_WHITE | COLOUR_FLAG_OUTLINE, TextAlignment::RIGHT }); diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 715122ae36..bf7fe484ca 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -42,7 +42,7 @@ // This string specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "13" +#define NETWORK_STREAM_VERSION "14" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION static Peep* _pickup_peep = nullptr; @@ -111,7 +111,6 @@ static u8string network_get_public_key_path(u8string_view playerName, u8string_v NetworkBase::NetworkBase(OpenRCT2::IContext& context) : OpenRCT2::System(context) { - wsa_initialized = false; mode = NETWORK_MODE_NONE; status = NETWORK_STATUS_NONE; last_ping_sent_time = 0; @@ -378,6 +377,8 @@ bool NetworkBase::BeginServer(uint16_t port, const std::string& address) ServerProviderEmail = gConfigNetwork.provider_email; ServerProviderWebsite = gConfigNetwork.provider_website; + IsServerPlayerInvisible = gOpenRCT2Headless; + CheatsReset(); LoadGroups(); BeginChatLog(); @@ -692,6 +693,18 @@ NetworkGroup* NetworkBase::GetGroupByID(uint8_t id) const return nullptr; } +int32_t NetworkBase::GetTotalNumPlayers() const noexcept +{ + return static_cast(player_list.size()); +} + +int32_t NetworkBase::GetNumVisiblePlayers() const noexcept +{ + if (IsServerPlayerInvisible) + return static_cast(player_list.size() - 1); + return static_cast(player_list.size()); +} + const char* NetworkBase::FormatChat(NetworkPlayer* fromplayer, const char* text) { static std::string formatted; @@ -1569,7 +1582,7 @@ json_t NetworkBase::GetServerInfoAsJson() const { json_t jsonObj = { { "name", gConfigNetwork.server_name }, { "requiresPassword", _password.size() > 0 }, - { "version", network_get_version() }, { "players", player_list.size() }, + { "version", network_get_version() }, { "players", GetNumVisiblePlayers() }, { "maxPlayers", gConfigNetwork.maxplayers }, { "description", gConfigNetwork.server_description }, { "greeting", gConfigNetwork.server_greeting }, { "dedicated", gOpenRCT2Headless }, }; @@ -1593,6 +1606,7 @@ void NetworkBase::Server_Send_GAMEINFO(NetworkConnection& connection) packet.WriteString(jsonObj.dump()); packet << _serverState.gamestateSnapshotsEnabled; + packet << IsServerPlayerInvisible; # endif connection.QueuePacket(std::move(packet)); @@ -2598,7 +2612,7 @@ void NetworkBase::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacke } } - if (static_cast(gConfigNetwork.maxplayers) <= player_list.size()) + if (GetNumVisiblePlayers() >= gConfigNetwork.maxplayers) { connection.AuthStatus = NetworkAuth::Full; log_info("Connection %s: Server is full.", hostName); @@ -3107,6 +3121,7 @@ void NetworkBase::Client_Handle_GAMEINFO([[maybe_unused]] NetworkConnection& con { auto jsonString = packet.ReadString(); packet >> _serverState.gamestateSnapshotsEnabled; + packet >> IsServerPlayerInvisible; json_t jsonData = Json::FromString(jsonString); @@ -3210,7 +3225,12 @@ uint8_t network_get_current_player_id() int32_t network_get_num_players() { - return static_cast(OpenRCT2::GetContext()->GetNetwork().player_list.size()); + return OpenRCT2::GetContext()->GetNetwork().GetTotalNumPlayers(); +} + +int32_t network_get_num_visible_players() +{ + return OpenRCT2::GetContext()->GetNetwork().GetNumVisiblePlayers(); } const char* network_get_player_name(uint32_t index) @@ -3742,6 +3762,11 @@ int32_t network_get_pickup_peep_old_x(uint8_t playerid) return -1; } +bool network_is_server_player_invisible() +{ + return OpenRCT2::GetContext()->GetNetwork().IsServerPlayerInvisible; +} + int32_t network_get_current_player_group_index() { auto& network = OpenRCT2::GetContext()->GetNetwork(); @@ -3976,6 +4001,10 @@ int32_t network_get_num_players() { return 1; } +int32_t network_get_num_visible_players() +{ + return 1; +} const char* network_get_player_name(uint32_t index) { return "local (OpenRCT2 compiled without MP)"; @@ -4127,6 +4156,10 @@ int32_t network_get_current_player_group_index() { return 0; } +bool network_is_server_player_invisible() +{ + return false; +} void network_append_chat_log(std::string_view) { } diff --git a/src/openrct2/network/NetworkBase.h b/src/openrct2/network/NetworkBase.h index ffc5790a13..e498158def 100644 --- a/src/openrct2/network/NetworkBase.h +++ b/src/openrct2/network/NetworkBase.h @@ -42,6 +42,8 @@ public: // Common auto GetGroupIteratorByID(uint8_t id) const; NetworkPlayer* GetPlayerByID(uint8_t id) const; NetworkGroup* GetGroupByID(uint8_t id) const; + int32_t GetTotalNumPlayers() const noexcept; + int32_t GetNumVisiblePlayers() const noexcept; void SetPassword(u8string_view password); uint8_t GetDefaultGroup() const noexcept; std::string BeginLog(const std::string& directory, const std::string& midName, const std::string& filenameFormat); @@ -177,6 +179,7 @@ public: // Public common std::string ServerProviderWebsite; std::vector> player_list; std::vector> group_list; + bool IsServerPlayerInvisible = false; private: // Common Data using CommandHandler = void (NetworkBase::*)(NetworkConnection& connection, NetworkPacket& packet); @@ -189,7 +192,6 @@ private: // Common Data uint8_t default_group = 0; bool _closeLock = false; bool _requireClose = false; - bool wsa_initialized = false; private: // Server Data std::unordered_map server_command_handlers; diff --git a/src/openrct2/network/NetworkServerAdvertiser.cpp b/src/openrct2/network/NetworkServerAdvertiser.cpp index 6837de79bd..63e04f5eb9 100644 --- a/src/openrct2/network/NetworkServerAdvertiser.cpp +++ b/src/openrct2/network/NetworkServerAdvertiser.cpp @@ -290,7 +290,7 @@ private: json_t GetHeartbeatJson() { - uint32_t numPlayers = network_get_num_players(); + uint32_t numPlayers = network_get_num_visible_players(); json_t root = { { "token", _token }, diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 851fcfa84e..73822e41c4 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -56,6 +56,7 @@ void network_flush(); [[nodiscard]] uint32_t network_get_server_tick(); [[nodiscard]] uint8_t network_get_current_player_id(); [[nodiscard]] int32_t network_get_num_players(); +[[nodiscard]] int32_t network_get_num_visible_players(); [[nodiscard]] const char* network_get_player_name(uint32_t index); [[nodiscard]] uint32_t network_get_player_flags(uint32_t index); [[nodiscard]] int32_t network_get_player_ping(uint32_t index); @@ -92,6 +93,7 @@ void network_set_pickup_peep(uint8_t playerid, Peep* peep); [[nodiscard]] Peep* network_get_pickup_peep(uint8_t playerid); void network_set_pickup_peep_old_x(uint8_t playerid, int32_t x); [[nodiscard]] int32_t network_get_pickup_peep_old_x(uint8_t playerid); +[[nodiscard]] bool network_is_server_player_invisible(); void network_send_chat(const char* text, const std::vector& playerIds = {}); void network_send_game_action(const GameAction* action);