diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index fe8db463bd..6c49fdf65b 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3834,4 +3834,6 @@ STR_5492 :Scenario options STR_6000 :Send Message STR_6001 :Type the message you would like to send. -STR_6002 :Player List +STR_6002 :Player List +STR_6003 :Player: +STR_6004 :Ping: diff --git a/src/drawing/drawing.h b/src/drawing/drawing.h index 49b36f6c57..64ce9c6018 100644 --- a/src/drawing/drawing.h +++ b/src/drawing/drawing.h @@ -135,6 +135,7 @@ void draw_string_right_underline(rct_drawpixelinfo *dpi, int format, void *args, int string_get_height_raw(char *buffer); void gfx_draw_string_centred_wrapped_partial(rct_drawpixelinfo *dpi, int x, int y, int width, int colour, rct_string_id format, void *args, int ticks); void gfx_draw_string_with_y_offsets(rct_drawpixelinfo *dpi, utf8 *text, int colour, int x, int y, const sint8 *yOffsets); +int gfx_clip_string(char* buffer, int width); bool ttf_initialise(); void ttf_dispose(); diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index d28473e43d..1ec59b969b 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -2031,6 +2031,8 @@ enum { STR_DEBUG_DROPDOWN_SCENARIO_OPTIONS = 5492, STR_PLAYER_LIST = 6002, + STR_PLAYER = 6003, + STR_PING = 6004, // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 diff --git a/src/network/network.cpp b/src/network/network.cpp index 3e0a04cde1..c1c17a36e2 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -51,6 +51,8 @@ enum { NETWORK_COMMAND_GAMECMD, NETWORK_COMMAND_TICK, NETWORK_COMMAND_PLAYERLIST, + NETWORK_COMMAND_PING, + NETWORK_COMMAND_PINGLIST, NETWORK_COMMAND_MAX }; @@ -125,6 +127,7 @@ NetworkPlayer::NetworkPlayer(const char* name) strncpy((char*)NetworkPlayer::name, name, sizeof(NetworkPlayer::name)); NetworkPlayer::name[sizeof(NetworkPlayer::name) - 1] = 0; ping = 0; + flags = 0; } NetworkConnection::NetworkConnection() @@ -216,6 +219,7 @@ Network::Network() wsa_initialized = false; mode = NETWORK_MODE_NONE; last_tick_sent_time = 0; + last_ping_sent_time = 0; strcpy(password, ""); client_command_handlers.resize(NETWORK_COMMAND_MAX, 0); client_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Client_Handle_AUTH; @@ -224,10 +228,13 @@ Network::Network() client_command_handlers[NETWORK_COMMAND_GAMECMD] = &Network::Client_Handle_GAMECMD; client_command_handlers[NETWORK_COMMAND_TICK] = &Network::Client_Handle_TICK; client_command_handlers[NETWORK_COMMAND_PLAYERLIST] = &Network::Client_Handle_PLAYERLIST; + client_command_handlers[NETWORK_COMMAND_PING] = &Network::Client_Handle_PING; + client_command_handlers[NETWORK_COMMAND_PINGLIST] = &Network::Client_Handle_PINGLIST; server_command_handlers.resize(NETWORK_COMMAND_MAX, 0); server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH; server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT; server_command_handlers[NETWORK_COMMAND_GAMECMD] = &Network::Server_Handle_GAMECMD; + server_command_handlers[NETWORK_COMMAND_PING] = &Network::Server_Handle_PING; } Network::~Network() @@ -348,9 +355,8 @@ bool Network::BeginServer(unsigned short port) return false; } - std::unique_ptr player(new NetworkPlayer("Server Player")); // change to make_unique in c++14 + NetworkPlayer* player = AddPlayer("Server Player"); player->flags |= NETWORK_PLAYER_FLAG_ISSERVER; - player_list.push_back(std::move(player)); printf("Ready for clients...\n"); @@ -405,6 +411,11 @@ void Network::Update() last_tick_sent_time = SDL_GetTicks(); Server_Send_TICK(); } + if (SDL_GetTicks() - last_ping_sent_time >= 3000) { + last_ping_sent_time = SDL_GetTicks(); + Server_Send_PING(); + Server_Send_PINGLIST(); + } SOCKET socket = accept(listening_socket, NULL, NULL); if (socket == INVALID_SOCKET) { if (WSAGetLastError() != WSAEWOULDBLOCK) { @@ -423,6 +434,14 @@ void Network::Update() } } +NetworkPlayer* Network::GetPlayerByID(int id) { + auto it = std::find_if(player_list.begin(), player_list.end(), [&id](std::unique_ptr const& player) { return player->id == id; }); + if (it != player_list.end()) { + return (*it).get(); + } + return 0; +} + void Network::Client_Send_AUTH(const char* gameversion, const char* name, const char* password) { std::unique_ptr packet = NetworkPacket::AllocatePacket(); @@ -510,6 +529,35 @@ void Network::Server_Send_PLAYERLIST() } } +void Network::Client_Send_PING() +{ + std::unique_ptr packet = NetworkPacket::AllocatePacket(); + *packet << (uint32)NETWORK_COMMAND_PING; + server_connection.QueuePacket(std::move(packet)); +} + +void Network::Server_Send_PING() +{ + std::unique_ptr packet = NetworkPacket::AllocatePacket(); + *packet << (uint32)NETWORK_COMMAND_PING; + for (auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { + (*it)->ping_time = SDL_GetTicks(); + (*it)->QueuePacket(NetworkPacket::DuplicatePacket(*packet)); + } +} + +void Network::Server_Send_PINGLIST() +{ + std::unique_ptr packet = NetworkPacket::AllocatePacket(); + *packet << (uint32)NETWORK_COMMAND_PINGLIST << (uint32)player_list.size(); + for (unsigned int i = 0; i < player_list.size(); i++) { + *packet << player_list[i]->id << player_list[i]->ping; + } + for (auto it = client_connection_list.begin(); it != client_connection_list.end(); it++) { + (*it)->QueuePacket(NetworkPacket::DuplicatePacket(*packet)); + } +} + bool Network::ProcessConnection(NetworkConnection& connection) { int packetStatus; @@ -592,6 +640,33 @@ void Network::RemoveClient(std::unique_ptr& connection) Server_Send_PLAYERLIST(); } +NetworkPlayer* Network::AddPlayer(const char* name) +{ + NetworkPlayer* addedplayer = 0; + int newid = -1; + if (GetMode() == NETWORK_MODE_SERVER) { + // Find first unused player id + for (int id = 0; id < 255; id++) { + if (std::find_if(player_list.begin(), player_list.end(), [&id](std::unique_ptr const& player) { return player->id == id; }) == player_list.end()) { + newid = id; + break; + } + } + } else { + newid = 0; + } + if (newid != -1) { + std::unique_ptr player(new NetworkPlayer(name)); // change to make_unique in c++14 + player->id = newid; + addedplayer = player.get(); + player_list.push_back(std::move(player)); + if (GetMode() == NETWORK_MODE_SERVER) { + Server_Send_PLAYERLIST(); + } + } + return addedplayer; +} + void Network::PrintError() { wchar_t *s = NULL; @@ -622,11 +697,8 @@ int Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& pa connection.authstatus = NETWORK_AUTH_BADPASSWORD; } else { connection.authstatus = NETWORK_AUTH_OK; - std::unique_ptr player(new NetworkPlayer(name)); // change to make_unique in c++14 - printf("New player: %s\n", player->name); - connection.player = player.get(); - player_list.push_back(std::move(player)); - Server_Send_PLAYERLIST(); + NetworkPlayer* player = AddPlayer(name); + connection.player = player; } std::unique_ptr responsepacket = NetworkPacket::AllocatePacket(); *responsepacket << (uint32)NETWORK_COMMAND_AUTH << (uint32)connection.authstatus; @@ -726,12 +798,47 @@ int Network::Client_Handle_PLAYERLIST(NetworkConnection& connection, NetworkPack packet >> size; for (unsigned int i = 0; i < size; i++) { const char* name = packet.ReadString(); - std::unique_ptr player(new NetworkPlayer(name)); // change to make_unique in c++14 - packet >> player->id >> player->flags; - if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) { - server_connection.player = player.get(); + NetworkPlayer* player = AddPlayer(name); + if (player) { + packet >> player->id >> player->flags; + if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) { + server_connection.player = player; + } + } + } + return 1; +} + +int Network::Client_Handle_PING(NetworkConnection& connection, NetworkPacket& packet) +{ + Client_Send_PING(); + return 1; +} + +int Network::Server_Handle_PING(NetworkConnection& connection, NetworkPacket& packet) +{ + int ping = SDL_GetTicks() - connection.ping_time; + if (ping < 0) { + ping = 0; + } + if (connection.player) { + connection.player->ping = ping; + } + return 1; +} + +int Network::Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket& packet) +{ + uint32 size; + packet >> size; + for (unsigned int i = 0; i < size; i++) { + uint8 id; + uint16 ping; + packet >> id >> ping; + NetworkPlayer* player = GetPlayerByID(id); + if (player) { + player->ping = ping; } - player_list.push_back(std::move(player)); } return 1; } @@ -786,6 +893,16 @@ const char* network_get_player_name(unsigned int index) return (const char*)gNetwork.player_list[index]->name; } +uint32 network_get_player_flags(unsigned int index) +{ + return gNetwork.player_list[index]->flags; +} + +int network_get_player_ping(unsigned int index) +{ + return gNetwork.player_list[index]->ping; +} + void network_send_map() { gNetwork.Server_Send_MAP(); diff --git a/src/network/network.h b/src/network/network.h index 2713d91f80..483496c588 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -111,6 +111,7 @@ public: NetworkPacket inboundpacket; int authstatus; NetworkPlayer* player; + uint32 ping_time; private: int SendPacket(NetworkPacket& packet); @@ -130,6 +131,7 @@ public: int GetAuthStatus(); uint32 GetServerTick(); void Update(); + NetworkPlayer* GetPlayerByID(int id); void Client_Send_AUTH(const char* gameversion, const char* name, const char* password); void Server_Send_MAP(); @@ -139,6 +141,9 @@ public: void Server_Send_GAMECMD(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); void Server_Send_TICK(); void Server_Send_PLAYERLIST(); + void Client_Send_PING(); + void Server_Send_PING(); + void Server_Send_PINGLIST(); std::vector> player_list; @@ -148,6 +153,7 @@ private: void ProcessGameCommandQueue(); void AddClient(SOCKET socket); void RemoveClient(std::unique_ptr& connection); + NetworkPlayer* AddPlayer(const char* name); void PrintError(); struct GameCommand @@ -166,6 +172,7 @@ private: SOCKET listening_socket; NetworkConnection server_connection; uint32 last_tick_sent_time; + uint32 last_ping_sent_time; uint32 server_tick; std::list> client_connection_list; std::multiset game_command_queue; @@ -184,6 +191,9 @@ private: int Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket& packet); int Client_Handle_TICK(NetworkConnection& connection, NetworkPacket& packet); int Client_Handle_PLAYERLIST(NetworkConnection& connection, NetworkPacket& packet); + int Client_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); + int Server_Handle_PING(NetworkConnection& connection, NetworkPacket& packet); + int Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket& packet); }; extern "C" { @@ -200,6 +210,8 @@ int network_get_authstatus(); uint32 network_get_server_tick(); int network_get_num_players(); const char* network_get_player_name(unsigned int index); +uint32 network_get_player_flags(unsigned int index); +int network_get_player_ping(unsigned int index); void network_send_map(); void network_send_chat(const char* text); diff --git a/src/windows/network.c b/src/windows/network.c index 19019acd3e..0b12f00b3c 100644 --- a/src/windows/network.c +++ b/src/windows/network.c @@ -83,12 +83,12 @@ enum WINDOW_PLAYER_LIST_WIDGET_IDX { }; static rct_widget window_player_list_widgets[] = { - { WWT_FRAME, 0, 0, 319, 0, 269, 0x0FFFFFFFF, 65535 }, // panel / background - { WWT_CAPTION, 0, 1, 318, 1, 14, STR_PLAYER_LIST, STR_WINDOW_TITLE_TIP }, // title bar - { WWT_CLOSEBOX, 0, 307, 317, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button - { WWT_RESIZE, 1, 0, 319, 43, 269, 0x0FFFFFFFF, STR_NONE }, // content panel + { WWT_FRAME, 0, 0, 339, 0, 239, 0x0FFFFFFFF, STR_NONE }, // panel / background + { WWT_CAPTION, 0, 1, 338, 1, 14, STR_PLAYER_LIST, STR_WINDOW_TITLE_TIP }, // title bar + { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button + { WWT_RESIZE, 1, 0, 339, 43, 239, 0x0FFFFFFFF, STR_NONE }, // content panel { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_NONE }, // tab - { WWT_SCROLL, 1, 3, 316, 72, 266, 3, 65535 }, // list + { WWT_SCROLL, 1, 3, 336, 60, 236, 2, STR_NONE }, // list { WIDGETS_END }, }; @@ -97,6 +97,7 @@ static void window_player_list_resize(rct_window *w); static void window_player_list_mousedown(int widgetIndex, rct_window* w, rct_widget* widget); static void window_player_list_update(rct_window *w); static void window_player_list_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height); +static void window_player_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y); static void window_player_list_invalidate(rct_window *w); static void window_player_list_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_player_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex); @@ -120,7 +121,7 @@ static rct_window_event_list window_player_list_events = { window_player_list_scrollgetsize, NULL, NULL, - NULL, + window_player_list_scrollmouseover, NULL, NULL, NULL, @@ -143,7 +144,7 @@ void window_player_list_open() if (window != NULL) return; - window = window_create_auto_pos(320, 270, &window_player_list_events, WC_PLAYER_LIST, WF_10 | WF_RESIZABLE); + window = window_create_auto_pos(320, 144, &window_player_list_events, WC_PLAYER_LIST, WF_10 | WF_RESIZABLE); window->widgets = window_player_list_widgets; window->enabled_widgets = 1 << WIDX_CLOSE; @@ -151,7 +152,7 @@ void window_player_list_open() window->selected_list_item = -1; window->frame_no = 0; window->min_width = 320; - window->min_height = 270; + window->min_height = 124; window->max_width = 500; window->max_height = 450; @@ -229,6 +230,18 @@ static void window_player_list_scrollgetsize(rct_window *w, int scrollIndex, int *width = 420; } +static void window_player_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y) +{ + int index; + + index = y / 10; + if (index >= w->no_list_items) + return; + + w->selected_list_item = index; + window_invalidate(w); +} + static void window_player_list_invalidate(rct_window *w) { for (int i = 0; i < 1; i++) @@ -248,6 +261,8 @@ static void window_player_list_invalidate(rct_window *w) static void window_player_list_paint(rct_window *w, rct_drawpixelinfo *dpi) { window_draw_widgets(w, dpi); + gfx_draw_string_left(dpi, STR_PLAYER, w, w->colours[2], w->x + 6, 58 - 12 + w->y + 1); + gfx_draw_string_left(dpi, STR_PING, w, w->colours[2], w->x + 246, 58 - 12 + w->y + 1); } static void window_player_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex) @@ -262,10 +277,32 @@ static void window_player_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi break; } - int format = 1191; - if (y + 11 >= dpi->y) { - gfx_draw_string(dpi, (char*)network_get_player_name(i), 255, 0, y - 1); + char buffer[300]; + int colour = 0; + if (i == w->selected_list_item) { + gfx_fill_rect(dpi, 0, y, 800, y + 9, 0x02000031); + strcpy(&buffer[0], network_get_player_name(i)); + colour = w->colours[2]; + } else { + buffer[0] = FORMAT_BLACK; + if (network_get_player_flags(i) & NETWORK_PLAYER_FLAG_ISSERVER) { + buffer[0] = FORMAT_BABYBLUE; + } + strcpy(&buffer[1], network_get_player_name(i)); + } + gfx_clip_string(buffer, 230); + gfx_draw_string(dpi, buffer, colour, 0, y - 1); + buffer[0] = FORMAT_RED; + int ping = network_get_player_ping(i); + if (ping <= 250) { + buffer[0] = FORMAT_YELLOW; + } + if (ping <= 100) { + buffer[0] = FORMAT_GREEN; + } + sprintf(&buffer[1], "%d ms", ping); + gfx_draw_string(dpi, buffer, colour, 240, y - 1); } y += 10; } @@ -275,7 +312,7 @@ static void window_player_list_refresh_list(rct_window *w) { window_invalidate(w); - w->no_list_items = 1; + w->no_list_items = network_get_num_players(); w->list_item_positions[0] = 0; w->selected_list_item = -1;