diff --git a/console_cmds.c b/console_cmds.c --- a/console_cmds.c +++ b/console_cmds.c @@ -381,7 +381,7 @@ DEF_CONSOLE_CMD(ConBan) } if (index == NETWORK_SERVER_INDEX) { - IConsolePrint(_icolour_def, "Silly boy, you can not ban yourself!"); + IConsoleError("Silly boy, you can not ban yourself!"); return true; } @@ -511,7 +511,7 @@ DEF_CONSOLE_CMD(ConStatus) const NetworkClientState *cs; if (argc == 0) { - IConsoleHelp("List the status of all clients connected to the server: Usage 'status'"); + IConsoleHelp("List the status of all clients connected to the server. Usage 'status'"); return true; } @@ -527,6 +527,35 @@ DEF_CONSOLE_CMD(ConStatus) return true; } +DEF_CONSOLE_CMD(ConServerInfo) +{ + const NetworkGameInfo *gi; + + if (argc == 0) { + IConsoleHelp("List current and maximum client/player limits. Usage 'server_info'"); + IConsoleHelp("You can change these values by setting the variables 'max_clients', 'max_companies' and 'max_spectators'"); + return true; + } + + gi = &_network_game_info; + IConsolePrintF(_icolour_def, "Current/maximum clients: %2d/%2d", gi->clients_on, gi->clients_max); + IConsolePrintF(_icolour_def, "Current/maximum companies: %2d/%2d", gi->companies_on, gi->companies_max); + IConsolePrintF(_icolour_def, "Current/maximum spectators: %2d/%2d", gi->spectators_on, gi->spectators_max); + + return true; +} + +DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount) { + /* XXX - hardcoded, string limiation -- TrueLight + * XXX - also see network.c:NetworkStartup ~1343 */ + if (_network_game_info.clients_max > 10) { + _network_game_info.clients_max = 10; + IConsoleError("Maximum clients is 10, truncating."); + } + + return true; +} + DEF_CONSOLE_CMD(ConKick) { NetworkClientInfo *ci; @@ -549,7 +578,7 @@ DEF_CONSOLE_CMD(ConKick) } if (index == NETWORK_SERVER_INDEX) { - IConsolePrint(_icolour_def, "Silly boy, you can not kick yourself!"); + IConsoleError("Silly boy, you can not kick yourself!"); return true; } @@ -1405,6 +1434,13 @@ void IConsoleStdLibRegister(void) IConsoleVarHookAdd("server_advertise", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); IConsoleVarHookAdd("server_advertise", ICONSOLE_HOOK_POST_ACTION, ConHookServerAdvertise); + IConsoleVarRegister("max_clients", &_network_game_info.clients_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of connected players during runtime. Default value: 10"); + IConsoleVarHookAdd("max_clients", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); + IConsoleVarHookAdd("max_clients", ICONSOLE_HOOK_POST_ACTION, ConHookValidateMaxClientsCount); + IConsoleVarRegister("max_companies", &_network_game_info.companies_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of active companies during runtime. Default value: 8"); + IConsoleVarHookAdd("max_companies", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); + IConsoleVarRegister("max_spectators", &_network_game_info.spectators_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of active spectators during runtime. Default value: 9"); + IConsoleVarHookAdd("max_spectators", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); IConsoleVarRegister("pause_on_join", &_network_pause_on_join, ICONSOLE_VAR_BOOLEAN, "Set if the server should pause gameplay while a client is joining. This might help slow users"); IConsoleVarHookAdd("pause_on_join", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); diff --git a/lang/english.txt b/lang/english.txt --- a/lang/english.txt +++ b/lang/english.txt @@ -1201,12 +1201,12 @@ STR_NETWORK_ADD_SERVER STR_NETWORK_ADD_SERVER_TIP :{BLACK}Adds a server to the list which will always be checked for running games. STR_NETWORK_ENTER_IP :{BLACK}Enter the address of the host -STR_NETWORK_CLIENTS_ONLINE :{BLACK}{COMMA}/{COMMA} +STR_NETWORK_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} - {COMMA}/{COMMA} STR_NETWORK_CLIENTS_CAPTION :{BLACK}Clients -STR_NETWORK_CLIENTS_CAPTION_TIP :{BLACK}Clients online / clients max +STR_NETWORK_CLIENTS_CAPTION_TIP :{BLACK}Clients online / clients max{}Companies online / companies max STR_NETWORK_GAME_INFO :{SILVER}GAME INFO STR_ORANGE :{ORANGE}{STRING} -STR_NETWORK_CLIENTS :{SILVER}Clients: {WHITE}{COMMA} / {COMMA} +STR_NETWORK_CLIENTS :{SILVER}Clients: {WHITE}{COMMA} / {COMMA} - {COMMA} / {COMMA} STR_NETWORK_LANGUAGE :{SILVER}Language: {WHITE}{STRING} STR_NETWORK_TILESET :{SILVER}Tileset: {WHITE}{STRING} STR_NETWORK_MAP_SIZE :{SILVER}Map size: {WHITE}{COMMA}x{COMMA} @@ -1230,7 +1230,7 @@ STR_NETWORK_SET_PASSWORD STR_NETWORK_PASSWORD_TIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible STR_NETWORK_SELECT_MAP :{BLACK}Select a map: STR_NETWORK_SELECT_MAP_TIP :{BLACK}Which map do you want to play? -STR_NETWORK_NUMBER_OF_CLIENTS :{BLACK}Maximum allowed clients: +STR_NETWORK_NUMBER_OF_CLIENTS :{BLACK}Max clients: STR_NETWORK_NUMBER_OF_CLIENTS_TIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled STR_NETWORK_COMBO1 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} STR_NETWORK_LAN :LAN @@ -1238,18 +1238,26 @@ STR_NETWORK_INTERNET STR_NETWORK_LAN_INTERNET :LAN / Internet STR_NETWORK_INTERNET_ADVERTISE :Internet (advertise) STR_NETWORK_COMBO2 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} -STR_NETWORK_2_CLIENTS :2 clients -STR_NETWORK_3_CLIENTS :3 clients -STR_NETWORK_4_CLIENTS :4 clients -STR_NETWORK_5_CLIENTS :5 clients -STR_NETWORK_6_CLIENTS :6 clients -STR_NETWORK_7_CLIENTS :7 clients -STR_NETWORK_8_CLIENTS :8 clients -STR_NETWORK_9_CLIENTS :9 clients -STR_NETWORK_10_CLIENTS :10 clients +STR_NETWORK_0_CLIENTS :0 players +STR_NETWORK_1_CLIENTS :1 player +STR_NETWORK_2_CLIENTS :2 players +STR_NETWORK_3_CLIENTS :3 players +STR_NETWORK_4_CLIENTS :4 players +STR_NETWORK_5_CLIENTS :5 players +STR_NETWORK_6_CLIENTS :6 players +STR_NETWORK_7_CLIENTS :7 players +STR_NETWORK_8_CLIENTS :8 players +STR_NETWORK_9_CLIENTS :9 players +STR_NETWORK_10_CLIENTS :10 players +STR_NETWORK_NUMBER_OF_COMPANIES :{BLACK}Max companies: +STR_NETWORK_NUMBER_OF_COMPANIES_TIP :{BLACK}Limit the server to a certain amount of companies +STR_NETWORK_COMBO3 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} +STR_NETWORK_NUMBER_OF_SPECTATORS :{BLACK}Max spectators: +STR_NETWORK_NUMBER_OF_SPECTATORS_TIP :{BLACK}Limit the server to a certain amount of spectators +STR_NETWORK_COMBO4 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} STR_NETWORK_LANGUAGE_SPOKEN :{BLACK}Language spoken: STR_NETWORK_LANGUAGE_TIP :{BLACK}Other players will know which language is spoken on the server -STR_NETWORK_COMBO3 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} +STR_NETWORK_COMBO5 :{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING} STR_NETWORK_START_GAME :{BLACK}Start Game STR_NETWORK_START_GAME_TIP :{BLACK}Start a new network game from a random map, or scenario STR_NETWORK_LOAD_GAME :{BLACK}Load Game @@ -1336,14 +1344,15 @@ STR_NETWORK_ERR_CLIENT_DESYNC STR_NETWORK_ERR_CLIENT_SAVEGAME :could not load map STR_NETWORK_ERR_CLIENT_CONNECTION_LOST :connection lost STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR :protocol error -STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED :Not Authorised +STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED :not authorized STR_NETWORK_ERR_CLIENT_NOT_EXPECTED :received strange packet STR_NETWORK_ERR_CLIENT_WRONG_REVISION :wrong revision STR_NETWORK_ERR_CLIENT_NAME_IN_USE :name already in use -STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD :wrong game-password +STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD :wrong password STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH :wrong player-id in DoCommand STR_NETWORK_ERR_CLIENT_KICKED :kicked by server STR_NETWORK_ERR_CLIENT_CHEATER :was trying to use a cheat +STR_NETWORK_ERR_CLIENT_SERVER_FULL :server full ############ End of leave-in-this-order STR_NETWORK_CLIENT_JOINED :has joined the game STR_NETWORK_GIVE_MONEY :gave your company some money ({CURRENCY}) diff --git a/network.c b/network.c --- a/network.c +++ b/network.c @@ -554,8 +554,11 @@ void NetworkCloseClient(NetworkClientSta if (_network_server) { // We just lost one client :( - if (cs->status > STATUS_INACTIVE) + if (cs->status > STATUS_INACTIVE) { _network_game_info.clients_on--; + if (DEREF_CLIENT_INFO(cs)->client_playas == OWNER_SPECTATOR) + _network_game_info.spectators_on--; + } _network_clients_connected--; while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) { @@ -784,9 +787,8 @@ static void NetworkInitialize(void) } // Clean the client_info memory - memset(_network_client_info, 0, sizeof(_network_client_info)); - memset(_network_player_info, 0, sizeof(_network_player_info)); - _network_lobby_company_count = 0; + memset(&_network_client_info, 0, sizeof(_network_client_info)); + memset(&_network_player_info, 0, sizeof(_network_player_info)); _sync_frame = 0; _network_first_time = true; @@ -914,16 +916,21 @@ static void NetworkInitGameInfo(void) if (_network_game_info.server_name[0] == '\0') snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server"); + ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision)); + // The server is a client too ;) if (_network_dedicated) { _network_game_info.clients_on = 0; + _network_game_info.companies_on = 0; _network_game_info.dedicated = true; } else { _network_game_info.clients_on = 1; + _network_game_info.companies_on = 1; _network_game_info.dedicated = false; } - ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision)); + _network_game_info.spectators_on = 0; + _network_game_info.game_date = _date; _network_game_info.start_date = ConvertIntDate(_patches.starting_date); _network_game_info.map_width = MapSizeX(); @@ -1332,14 +1339,12 @@ void NetworkStartUp(void) snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip)); /* Generate an unique id when there is none yet */ - if (_network_unique_id[0] == '\0') - NetworkGenerateUniqueId(); + if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId(); memset(&_network_game_info, 0, sizeof(_network_game_info)); - - /* XXX - Hard number here, because the strings can currently handle no more - than 10 clients -- TrueLight */ - _network_game_info.clients_max = 10; + _network_game_info.clients_max = 10; // XXX - hardcoded, string limiation -- TrueLight + _network_game_info.companies_max = MAX_PLAYERS; + _network_game_info.spectators_max = _network_game_info.clients_max; // Let's load the network in windows #if defined(WIN32) diff --git a/network.h b/network.h --- a/network.h +++ b/network.h @@ -61,25 +61,28 @@ enum { // some fields will be empty on the client (like game_password) by default // and only filled with data a player enters. typedef struct NetworkGameInfo { - char server_name[NETWORK_NAME_LENGTH]; // Server name - char hostname[NETWORK_HOSTNAME_LENGTH]; // Hostname of the server (if any) - char server_revision[NETWORK_REVISION_LENGTH]; // The SVN version number the server is using (e.g.: 'r304') - // It even shows a SVN version in release-version, so - // it is easy to compare if a server is of the correct version - byte server_lang; // Language of the server (we should make a nice table for this) - byte use_password; // Is set to != 0 if it uses a password - char server_password[NETWORK_PASSWORD_LENGTH]; // On the server: the game password, on the client: != "" if server has password - byte clients_max; // Max clients allowed on server - byte clients_on; // Current count of clients on server - byte spectators_on; // How many spectators do we have? - uint16 game_date; // Current date - uint16 start_date; // When the game started - char map_name[NETWORK_NAME_LENGTH]; // Map which is played ["random" for a randomized map] - uint16 map_width; // Map width - uint16 map_height; // Map height - byte map_set; // Graphical set - bool dedicated; // Is this a dedicated server? - char rcon_password[NETWORK_PASSWORD_LENGTH]; // RCon password for the server. "" if rcon is disabled + char server_name[NETWORK_NAME_LENGTH]; // Server name + char hostname[NETWORK_HOSTNAME_LENGTH]; // Hostname of the server (if any) + char server_revision[NETWORK_REVISION_LENGTH]; // The SVN version number the server is using (e.g.: 'r304') + // It even shows a SVN version in release-version, so + // it is easy to compare if a server is of the correct version + byte server_lang; // Language of the server (we should make a nice table for this) + byte use_password; // Is set to != 0 if it uses a password + char server_password[NETWORK_PASSWORD_LENGTH]; // On the server: the game password, on the client: != "" if server has password + byte clients_max; // Max clients allowed on server + byte clients_on; // Current count of clients on server + byte companies_max; // Max companies allowed on server + byte companies_on; // How many started companies do we have + byte spectators_max; // Max spectators allowed on server + byte spectators_on; // How many spectators do we have? + uint16 game_date; // Current date + uint16 start_date; // When the game started + char map_name[NETWORK_NAME_LENGTH]; // Map which is played ["random" for a randomized map] + uint16 map_width; // Map width + uint16 map_height; // Map height + byte map_set; // Graphical set + bool dedicated; // Is this a dedicated server? + char rcon_password[NETWORK_PASSWORD_LENGTH]; // RCon password for the server. "" if rcon is disabled } NetworkGameInfo; typedef struct NetworkPlayerInfo { diff --git a/network_client.c b/network_client.c --- a/network_client.c +++ b/network_client.c @@ -395,20 +395,30 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER { NetworkErrorCode error = NetworkRecv_uint8(MY_CLIENT, p); - if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || - error == NETWORK_ERROR_PLAYER_MISMATCH) { - // We made an error in the protocol, and our connection is closed.... :( - _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR; - } else if (error == NETWORK_ERROR_WRONG_REVISION) { - // Wrong revision :( - _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION; - } else if (error == NETWORK_ERROR_WRONG_PASSWORD) { - // Wrong password - _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD; - } else if (error == NETWORK_ERROR_KICKED) { - _switch_mode_errorstr = STR_NETWORK_ERR_KICKED; - } else if (error == NETWORK_ERROR_CHEATER) { - _switch_mode_errorstr = STR_NETWORK_ERR_CHEATER; + switch (error) { + /* We made an error in the protocol, and our connection is closed.... */ + case NETWORK_ERROR_NOT_AUTHORIZED: + case NETWORK_ERROR_NOT_EXPECTED: + case NETWORK_ERROR_PLAYER_MISMATCH: + _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR; + break; + case NETWORK_ERROR_FULL: + _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL; + break; + case NETWORK_ERROR_WRONG_REVISION: + _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION; + break; + case NETWORK_ERROR_WRONG_PASSWORD: + _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD; + break; + case NETWORK_ERROR_KICKED: + _switch_mode_errorstr = STR_NETWORK_ERR_KICKED; + break; + case NETWORK_ERROR_CHEATER: + _switch_mode_errorstr = STR_NETWORK_ERR_CHEATER; + break; + default: + _switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION; } DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); diff --git a/network_data.h b/network_data.h --- a/network_data.h +++ b/network_data.h @@ -18,7 +18,7 @@ #define NETWORK_EMPTY_INDEX 0 // What version of game-info do we use? -#define NETWORK_GAME_INFO_VERSION 1 +#define NETWORK_GAME_INFO_VERSION 2 // What version of company info is this? #define NETWORK_COMPANY_INFO_VERSION 3 // What version of master-server-protocol do we use? @@ -92,6 +92,7 @@ typedef enum { NETWORK_ERROR_PLAYER_MISMATCH, // Happens in CLIENT_COMMAND NETWORK_ERROR_KICKED, NETWORK_ERROR_CHEATER, + NETWORK_ERROR_FULL, } NetworkErrorCode; // Actions that can be used for NetworkTextMessage diff --git a/network_gui.c b/network_gui.c --- a/network_gui.c +++ b/network_gui.c @@ -45,6 +45,29 @@ static const StringID _lan_internet_type INVALID_STRING_ID }; +static const StringID _players_dropdown[] = { + STR_NETWORK_0_CLIENTS, + STR_NETWORK_1_CLIENTS, + STR_NETWORK_2_CLIENTS, + STR_NETWORK_3_CLIENTS, + STR_NETWORK_4_CLIENTS, + STR_NETWORK_5_CLIENTS, + STR_NETWORK_6_CLIENTS, + STR_NETWORK_7_CLIENTS, + STR_NETWORK_8_CLIENTS, + STR_NETWORK_9_CLIENTS, + STR_NETWORK_10_CLIENTS, + INVALID_STRING_ID +}; + +static const StringID _language_dropdown[] = { + STR_NETWORK_LANG_ANY, + STR_NETWORK_LANG_ENGLISH, + STR_NETWORK_LANG_GERMAN, + STR_NETWORK_LANG_FRENCH, + INVALID_STRING_ID +}; + enum { NET_PRC__OFFSET_TOP_WIDGET = 74, NET_PRC__OFFSET_TOP_WIDGET_COMPANY = 42, @@ -88,8 +111,11 @@ static void NetworkGameWindowWndProc(Win SETBIT(w->disabled_state, 17); SETBIT(w->disabled_state, 18); } else if (!sel->online) { SETBIT(w->disabled_state, 17); // Server offline, join button disabled - } else if (sel->info.clients_on == sel->info.clients_max) { + } else if (sel->info.clients_on >= sel->info.clients_max) { SETBIT(w->disabled_state, 17); // Server full, join button disabled + } else if (sel->info.companies_on >= sel->info.companies_max && + sel->info.spectators_on >= sel->info.spectators_max) { + SETBIT(w->disabled_state, 17); // revisions don't match, check if server has no revision; then allow connection } else if (strncmp(sel->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) != 0) { @@ -106,9 +132,6 @@ static void NetworkGameWindowWndProc(Win DrawString(9, 23, STR_NETWORK_PLAYER_NAME, 2); DrawString(9, 43, STR_NETWORK_CONNECTION, 2); - DrawString(15, 63, STR_NETWORK_GAME_NAME, 2); - DrawString(135, 63, STR_NETWORK_CLIENTS_CAPTION, 2); - { // draw list of games uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; int32 n = 0; @@ -133,7 +156,9 @@ static void NetworkGameWindowWndProc(Win SetDParam(0, cur_item->info.clients_on); SetDParam(1, cur_item->info.clients_max); - DrawString(135, y, STR_NETWORK_CLIENTS_ONLINE, 2); + SetDParam(2, cur_item->info.companies_on); + SetDParam(3, cur_item->info.companies_max); + DrawString(135, y, STR_NETWORK_GENERAL_ONLINE, 2); // only draw icons if the server is online if (cur_item->online) { @@ -176,10 +201,12 @@ static void NetworkGameWindowWndProc(Win SetDParam(0, sel->info.clients_on); SetDParam(1, sel->info.clients_max); - DrawString(260, y, STR_NETWORK_CLIENTS, 2); // clients on the server / maximum slots + SetDParam(2, sel->info.companies_on); + SetDParam(3, sel->info.companies_max); + DrawString(260, y, STR_NETWORK_CLIENTS, 2); y += 10; - SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); + SetDParam(0, _language_dropdown[sel->info.server_lang]); DrawString(260, y, STR_NETWORK_LANGUAGE, 2); // server language y += 10; @@ -339,40 +366,40 @@ static void NetworkGameWindowWndProc(Win } static const Widget _network_game_window_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 489, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, -{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 489, 14, 214, 0x0, STR_NULL}, +{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 549, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, +{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 549, 14, 214, STR_NULL, STR_NULL}, /* LEFT SIDE */ -{ WWT_IMGBTN, RESIZE_NONE, BGC, 90, 231, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP}, +{ WWT_IMGBTN, RESIZE_NONE, BGC, 90, 291, 22, 33, STR_NULL, STR_NETWORK_ENTER_NAME_TIP}, -{ WWT_6, RESIZE_NONE, BGC, 90, 231, 42, 53, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, -{ WWT_TEXTBTN, RESIZE_NONE, BGC, 220, 230, 43, 52, STR_0225, STR_NETWORK_CONNECTION_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 90, 231, 42, 53, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 220, 230, 43, 52, STR_0225, STR_NETWORK_CONNECTION_TIP}, -{ WWT_IMGBTN, RESIZE_NONE, BTC, 10, 130, 62, 73, 0x0, STR_NETWORK_GAME_NAME_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, BTC, 131, 180, 62, 73, 0x0, STR_NETWORK_CLIENTS_CAPTION_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, BTC, 181, 219, 62, 73, 0x0, STR_NETWORK_INFO_ICONS_TIP }, +{ WWT_TEXTBTN, RESIZE_NONE, BTC, 10, 130, 62, 73, STR_NETWORK_GAME_NAME, STR_NETWORK_GAME_NAME_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BTC, 131, 180, 62, 73, STR_NETWORK_CLIENTS_CAPTION,STR_NETWORK_CLIENTS_CAPTION_TIP}, +{ WWT_PANEL, RESIZE_NONE, BTC, 181, 219, 62, 73, 0, STR_NETWORK_INFO_ICONS_TIP}, -{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 219, 74, 185, 0x801, STR_NETWORK_CLICK_GAME_TO_SELECT}, -{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 220, 231, 62, 185, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 219, 74, 185, 0x801, STR_NETWORK_CLICK_GAME_TO_SELECT}, +{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 220, 231, 62, 185, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 115, 195, 206, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 125, 231, 195, 206, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 250, 360, 195, 206, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 480, 195, 206, STR_012E_CANCEL, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 115, 195, 206, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 125, 231, 195, 206, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 250, 360, 195, 206, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 480, 195, 206, STR_012E_CANCEL, STR_NULL}, /* RIGHT SIDE */ -{ WWT_IMGBTN, RESIZE_NONE, BGC, 250, 480, 22, 185, 0x0, STR_NULL}, -{ WWT_6, RESIZE_NONE, BGC, 251, 479, 23, 184, 0x0, STR_NULL}, +{ WWT_IMGBTN, RESIZE_NONE, BGC, 250, 480, 22, 185, STR_NULL, STR_NULL}, +{ WWT_6, RESIZE_NONE, BGC, 251, 479, 23, 184, STR_NULL, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 257, 360, 164, 175, STR_NETWORK_JOIN_GAME, STR_NULL}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 473, 164, 175, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 257, 360, 164, 175, STR_NETWORK_JOIN_GAME, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 473, 164, 175, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, { WIDGETS_END}, }; static const WindowDesc _network_game_window_desc = { - WDP_CENTER, WDP_CENTER, 490, 215, + WDP_CENTER, WDP_CENTER, 550, 215, WC_NETWORK_WINDOW,0, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_game_window_widgets, @@ -408,27 +435,6 @@ void ShowNetworkGameWindow(void) UpdateNetworkGameWindow(true); } -static const StringID _players_dropdown[] = { - STR_NETWORK_2_CLIENTS, - STR_NETWORK_3_CLIENTS, - STR_NETWORK_4_CLIENTS, - STR_NETWORK_5_CLIENTS, - STR_NETWORK_6_CLIENTS, - STR_NETWORK_7_CLIENTS, - STR_NETWORK_8_CLIENTS, - STR_NETWORK_9_CLIENTS, - STR_NETWORK_10_CLIENTS, - INVALID_STRING_ID -}; - -static const StringID _language_dropdown[] = { - STR_NETWORK_LANG_ANY, - STR_NETWORK_LANG_ENGLISH, - STR_NETWORK_LANG_GERMAN, - STR_NETWORK_LANG_FRENCH, - INVALID_STRING_ID -}; - enum { NSSWND_START = 64, NSSWND_ROWSIZE = 12 @@ -446,21 +452,25 @@ static void NetworkStartServerWindowWndP int y = NSSWND_START, pos; const FiosItem *item; - SetDParam(7, STR_NETWORK_LAN_INTERNET + _network_advertise); - SetDParam(9, STR_NETWORK_2_CLIENTS + _network_game_info.clients_max - 2); - SetDParam(11, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); + SetDParam( 7, _connection_types_dropdown[_network_advertise]); + SetDParam( 9, _players_dropdown[_network_game_info.clients_max]); + SetDParam(11, _players_dropdown[_network_game_info.companies_max]); + SetDParam(13, _players_dropdown[_network_game_info.spectators_max]); + SetDParam(15, _language_dropdown[_network_game_info.server_lang]); DrawWindowWidgets(w); - GfxFillRect(11, 63, 259, 171, 0xD7); - + GfxFillRect(11, 63, 258, 215, 0xD7); DrawEditBox(w, 3); DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2); DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2); - DrawString(280, 63, STR_NETWORK_CONNECTION, 2); - DrawString(280, 95, STR_NETWORK_NUMBER_OF_CLIENTS, 2); - DrawString(280, 127, STR_NETWORK_LANGUAGE_SPOKEN, 2); + + DrawString(280, 63, STR_NETWORK_CONNECTION, 2); + DrawString(280, 95, STR_NETWORK_NUMBER_OF_CLIENTS, 2); + DrawString(280, 127, STR_NETWORK_NUMBER_OF_COMPANIES, 2); + DrawString(280, 159, STR_NETWORK_NUMBER_OF_SPECTATORS, 2); + DrawString(280, 191, STR_NETWORK_LANGUAGE_SPOKEN, 2); if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3); @@ -469,7 +479,7 @@ static void NetworkStartServerWindowWndP while (pos < _fios_num + 1) { item = _fios_list + pos - 1; if (item == _selected_map || (pos == 0 && _selected_map == NULL)) - GfxFillRect(11, y - 1, 259, y + 10, 155); // show highlighted item with a different colour + GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour if (pos == 0) DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9); else DoDrawString(item->title, 14, y, _fios_colors[item->type] ); @@ -484,7 +494,7 @@ static void NetworkStartServerWindowWndP _selected_field = e->click.widget; switch (e->click.widget) { case 0: /* Close 'X' */ - case 15: /* Cancel button */ + case 19: /* Cancel button */ ShowNetworkGameWindow(); break; @@ -505,13 +515,19 @@ static void NetworkStartServerWindowWndP case 7: case 8: /* Connection type */ ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8 break; - case 9: case 10: /* Number of Players */ - ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max - 2, 10, 0, 0); // do it for widget 10 - return; - case 11: case 12: /* Language */ - ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 12, 0, 0); // do it for widget 12 - return; - case 13: /* Start game */ + case 9: case 10: /* Number of Players (hide 0 and 1 players) */ + ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max, 10, 0, 3); + break; + case 11: case 12: /* Number of Companies (hide 0, 9 and 10 companies; max is 8) */ + ShowDropDownMenu(w, _players_dropdown, _network_game_info.companies_max, 12, 0, 1537); + break; + case 13: case 14: /* Number of Spectators */ + ShowDropDownMenu(w, _players_dropdown, _network_game_info.spectators_max, 14, 0, 0); + break; + case 15: case 16: /* Language */ + ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 16, 0, 0); + break; + case 17: /* Start game */ _is_network_server = true; ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name)); UpdateTextBufferSize(&WP(w, querystr_d).text); @@ -529,7 +545,7 @@ static void NetworkStartServerWindowWndP } } break; - case 14: /* Load game */ + case 18: /* Load game */ _is_network_server = true; ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name)); UpdateTextBufferSize(&WP(w, querystr_d).text); @@ -543,15 +559,11 @@ static void NetworkStartServerWindowWndP case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ switch(e->dropdown.button) { - case 8: - _network_advertise = (e->dropdown.index != 0); - break; - case 10: - _network_game_info.clients_max = e->dropdown.index + 2; - break; - case 12: - _network_game_info.server_lang = e->dropdown.index; - break; + case 8: _network_advertise = (e->dropdown.index != 0); break; + case 10: _network_game_info.clients_max = e->dropdown.index; break; + case 12: _network_game_info.companies_max = e->dropdown.index; break; + case 14: _network_game_info.spectators_max = e->dropdown.index; break; + case 16: _network_game_info.server_lang = e->dropdown.index; break; } SetWindowDirty(w); @@ -574,33 +586,35 @@ static void NetworkStartServerWindowWndP } static const Widget _network_start_server_window_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, -{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 199, 0x0, STR_NULL}, +{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, +{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, +{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 243, 0x0, STR_NULL}, -{ WWT_IMGBTN, RESIZE_NONE, BGC, 100, 272, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 285, 405, 22, 33, STR_NETWORK_SET_PASSWORD, STR_NETWORK_PASSWORD_TIP}, - -{ WWT_6, RESIZE_NONE, BGC, 10, 271, 62, 172, 0x0, STR_NETWORK_SELECT_MAP_TIP}, -{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 260, 271, 63, 171, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_IMGBTN, RESIZE_NONE, BGC, 100, 272, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 285, 405, 22, 33, STR_NETWORK_SET_PASSWORD, STR_NETWORK_PASSWORD_TIP}, -{ WWT_6, RESIZE_NONE, BGC, 280, 410, 77, 88, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, -{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 78, 87, STR_0225, STR_NETWORK_CONNECTION_TIP}, - -{ WWT_6, RESIZE_NONE, BGC, 280, 410, 109, 120, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, -{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 110, 119, STR_0225, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 10, 271, 62, 216, 0x0, STR_NETWORK_SELECT_MAP_TIP}, +{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 259, 270, 63, 215, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +/* Combo boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */ +{ WWT_6, RESIZE_NONE, BGC, 280, 410, 77, 88, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 78, 87, STR_0225, STR_NETWORK_CONNECTION_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 280, 410, 109, 120, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 110, 119, STR_0225, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 280, 410, 141, 152, STR_NETWORK_COMBO3, STR_NETWORK_NUMBER_OF_COMPANIES_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 142, 151, STR_0225, STR_NETWORK_NUMBER_OF_COMPANIES_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 280, 410, 173, 184, STR_NETWORK_COMBO4, STR_NETWORK_NUMBER_OF_SPECTATORS_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 174, 183, STR_0225, STR_NETWORK_NUMBER_OF_SPECTATORS_TIP}, +{ WWT_6, RESIZE_NONE, BGC, 280, 410, 205, 216, STR_NETWORK_COMBO5, STR_NETWORK_LANGUAGE_TIP}, +{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 206, 215, STR_0225, STR_NETWORK_LANGUAGE_TIP}, -{ WWT_6, RESIZE_NONE, BGC, 280, 410, 141, 152, STR_NETWORK_COMBO3, STR_NETWORK_LANGUAGE_TIP}, -{ WWT_TEXTBTN, RESIZE_NONE, BGC, 399, 409, 142, 151, STR_0225, STR_NETWORK_LANGUAGE_TIP}, - -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 40, 140, 180, 191, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 150, 250, 180, 191, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP}, -{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 260, 360, 180, 191, STR_012E_CANCEL, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 40, 140, 224, 235, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 150, 250, 224, 235, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP}, +{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 260, 360, 224, 235, STR_012E_CANCEL, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _network_start_server_window_desc = { - WDP_CENTER, WDP_CENTER, 420, 200, + WDP_CENTER, WDP_CENTER, 420, 244, WC_NETWORK_WINDOW,0, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_start_server_window_widgets, @@ -645,17 +659,22 @@ static byte NetworkLobbyFindCompanyIndex static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e) { switch (e->event) { + case WE_CREATE: + _selected_company_item = -1; + break; + case WE_PAINT: { + const NetworkGameInfo *gi = &_network_game_info; int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos; w->disabled_state = (_selected_company_item == -1) ? 1 << 7 : 0; + assert(_network_lobby_company_count == gi->companies_on); - if (_network_lobby_company_count == MAX_PLAYERS) - SETBIT(w->disabled_state, 8); + if (gi->companies_on == gi->companies_max) SETBIT(w->disabled_state, 8); + if (gi->spectators_on == gi->spectators_max) SETBIT(w->disabled_state, 9); /* You can not join a server as spectator when it has no companies active.. - it causes some nasty crashes */ - if (_network_lobby_company_count == 0) - SETBIT(w->disabled_state, 9); + * it causes some nasty crashes */ + if (gi->companies_on == 0) SETBIT(w->disabled_state, 9); DrawWindowWidgets(w); @@ -665,13 +684,13 @@ static void NetworkLobbyWindowWndProc(Wi // draw company list GfxFillRect(11, 41, 154, 165, 0xD7); pos = w->vscroll.pos; - while (pos < _network_lobby_company_count) { + while (pos < gi->companies_on) { byte index = NetworkLobbyFindCompanyIndex(pos); bool income = false; if (_selected_company_item == index) GfxFillRect(11, y - 1, 154, y + 10, 155); // show highlighted item with a different colour - DoDrawString(_network_player_info[index].company_name, 13, y, 2); + DoDrawStringTruncated(_network_player_info[index].company_name, 13, y, 2, 135 - 13); if (_network_player_info[index].use_password != 0) DrawSprite(SPR_LOCK, 135, y); @@ -690,10 +709,11 @@ static void NetworkLobbyWindowWndProc(Wi if (_selected_company_item != -1) { // if a company is selected... // show company info const uint x = 183; + const uint trunc_width = w->widget[6].right - x; y = 65; SetDParamStr(0, _network_player_info[_selected_company_item].company_name); - DrawString(x, y, STR_NETWORK_COMPANY_NAME, 2); + DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, 2, trunc_width); y += 10; SetDParam(0, _network_player_info[_selected_company_item].inaugurated_year + MAX_YEAR_BEGIN_REAL); @@ -733,7 +753,7 @@ static void NetworkLobbyWindowWndProc(Wi y += 10; SetDParamStr(0, _network_player_info[_selected_company_item].players); - DrawString(x, y, STR_NETWORK_PLAYERS, 2); // players + DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, 2, trunc_width); // players y += 10; } } break; @@ -753,7 +773,7 @@ static void NetworkLobbyWindowWndProc(Wi return; } _selected_company_item += w->vscroll.pos; - if (_selected_company_item >= _network_lobby_company_count) { + if (_selected_company_item >= _network_game_info.companies_on) { _selected_company_item = -1; SetWindowDirty(w); return; @@ -781,9 +801,6 @@ static void NetworkLobbyWindowWndProc(Wi NetworkQueryServer(_network_last_host, _network_last_port, false); break; } break; - - case WE_CREATE: - _selected_company_item = -1; } } diff --git a/network_server.c b/network_server.c --- a/network_server.c +++ b/network_server.c @@ -156,13 +156,13 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SER NetworkSend_uint8(p, error); NetworkSend_Packet(p, cs); + GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + error); + // Only send when the current client was in game if (cs->status > STATUS_AUTH) { NetworkGetClientName(client_name, sizeof(client_name), cs); - GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + error); - - DEBUG(net, 2)("[NET] %s made an error (%s) and his connection is closed", client_name, str); + DEBUG(net, 2) ("[NET] '%s' made an error and has been disconnected. Reason: %s", client_name, str); NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str); @@ -177,7 +177,7 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SER } } } else { - DEBUG(net, 2)("[NET] Clientno %d has made an error and his connection is closed", cs->index); + DEBUG(net, 2) ("[NET] Client %d made an error and has been disconnected. Reason: %s", cs->index, str); } cs->quited = true; @@ -213,14 +213,16 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WE // Packet *p; - NetworkClientState *new_cs; + const NetworkClientState *new_cs; + const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); // Invalid packet when status is AUTH or higher - if (cs->status >= STATUS_AUTH) - return; + if (cs->status >= STATUS_AUTH) return; cs->status = STATUS_AUTH; _network_game_info.clients_on++; + /* increment spectator; defer company addition for when player is actually created */ + if (ci->client_playas == OWNER_SPECTATOR) _network_game_info.spectators_on++; p = NetworkSend_Init(PACKET_SERVER_WELCOME); NetworkSend_uint16(p, cs->index); @@ -585,12 +587,10 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT char name[NETWORK_NAME_LENGTH]; char unique_id[NETWORK_NAME_LENGTH]; NetworkClientInfo *ci; - char test_name[NETWORK_NAME_LENGTH]; byte playas; NetworkLanguage client_lang; char client_revision[NETWORK_REVISION_LENGTH]; - NetworkRecv_string(cs, p, client_revision, sizeof(client_revision)); #if defined(WITH_REV) || defined(WITH_REV_HACK) @@ -610,18 +610,28 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT client_lang = NetworkRecv_uint8(cs, p); NetworkRecv_string(cs, p, unique_id, sizeof(unique_id)); - if (cs->quited) - return; + if (cs->quited) return; - // Check if someone else already has that name - snprintf(test_name, sizeof(test_name), "%s", name); - - if (test_name[0] == '\0') { - // We need a valid name.. make it Player - snprintf(test_name, sizeof(test_name), "Player"); + // join another company does not affect these values + switch (playas) { + case 0: /* New company */ + if (_network_game_info.companies_max >= _network_game_info.companies_on) { + SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL); + return; + } + break; + case OWNER_SPECTATOR: /* Spectator */ + if (_network_game_info.spectators_on >= _network_game_info.spectators_max) { + SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL); + return; + } + break; } - if (!NetworkFindName(test_name)) { + // We need a valid name.. make it Player + if (name[0] == '\0') snprintf(name, sizeof(name), "Player"); + + if (!NetworkFindName(name)) { // Change name if duplicate // We could not create a name for this player SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NAME_IN_USE); return; @@ -629,7 +639,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT ci = DEREF_CLIENT_INFO(cs); - snprintf(ci->client_name, sizeof(ci->client_name), "%s", test_name); + snprintf(ci->client_name, sizeof(ci->client_name), "%s", name); snprintf(ci->unique_id, sizeof(ci->unique_id), "%s", unique_id); ci->client_playas = playas; ci->client_lang = client_lang; diff --git a/network_udp.c b/network_udp.c --- a/network_udp.c +++ b/network_udp.c @@ -61,6 +61,13 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIEN _network_game_info.map_set = _opt.landscape; NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION); + + /* NETWORK_GAME_INFO_VERSION = 2 */ + NetworkSend_uint8 (packet, _network_game_info.companies_max); + NetworkSend_uint8 (packet, _network_game_info.companies_on); + NetworkSend_uint8 (packet, _network_game_info.spectators_max); + + /* NETWORK_GAME_INFO_VERSION = 1 */ NetworkSend_string(packet, _network_game_info.server_name); NetworkSend_string(packet, _network_game_info.server_revision); NetworkSend_uint8 (packet, _network_game_info.server_lang); @@ -103,30 +110,39 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVE // Find next item item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port)); - if (game_info_version == 1) { - NetworkRecv_string(&_udp_cs, p, item->info.server_name, sizeof(item->info.server_name)); - NetworkRecv_string(&_udp_cs, p, item->info.server_revision, sizeof(item->info.server_revision)); - item->info.server_lang = NetworkRecv_uint8(&_udp_cs, p); - item->info.use_password = NetworkRecv_uint8(&_udp_cs, p); - item->info.clients_max = NetworkRecv_uint8(&_udp_cs, p); - item->info.clients_on = NetworkRecv_uint8(&_udp_cs, p); - item->info.spectators_on = NetworkRecv_uint8(&_udp_cs, p); - item->info.game_date = NetworkRecv_uint16(&_udp_cs, p); - item->info.start_date = NetworkRecv_uint16(&_udp_cs, p); - NetworkRecv_string(&_udp_cs, p, item->info.map_name, sizeof(item->info.map_name)); - item->info.map_width = NetworkRecv_uint16(&_udp_cs, p); - item->info.map_height = NetworkRecv_uint16(&_udp_cs, p); - item->info.map_set = NetworkRecv_uint8(&_udp_cs, p); - item->info.dedicated = NetworkRecv_uint8(&_udp_cs, p); + /* Please observe the order. In the order in which packets are sent + * they are to be received */ + switch (game_info_version) { + case 2: + item->info.companies_max = NetworkRecv_uint8(&_udp_cs, p); + item->info.companies_on = NetworkRecv_uint8(&_udp_cs, p); + item->info.spectators_max = NetworkRecv_uint8(&_udp_cs, p); + /* Fallthrough */ + case 1: + NetworkRecv_string(&_udp_cs, p, item->info.server_name, sizeof(item->info.server_name)); + NetworkRecv_string(&_udp_cs, p, item->info.server_revision, sizeof(item->info.server_revision)); + item->info.server_lang = NetworkRecv_uint8(&_udp_cs, p); + item->info.use_password = NetworkRecv_uint8(&_udp_cs, p); + item->info.clients_max = NetworkRecv_uint8(&_udp_cs, p); + item->info.clients_on = NetworkRecv_uint8(&_udp_cs, p); + item->info.spectators_on = NetworkRecv_uint8(&_udp_cs, p); + item->info.game_date = NetworkRecv_uint16(&_udp_cs, p); + item->info.start_date = NetworkRecv_uint16(&_udp_cs, p); + NetworkRecv_string(&_udp_cs, p, item->info.map_name, sizeof(item->info.map_name)); + item->info.map_width = NetworkRecv_uint16(&_udp_cs, p); + item->info.map_height = NetworkRecv_uint16(&_udp_cs, p); + item->info.map_set = NetworkRecv_uint8(&_udp_cs, p); + item->info.dedicated = NetworkRecv_uint8(&_udp_cs, p); - str_validate(item->info.server_name); - str_validate(item->info.server_revision); - str_validate(item->info.map_name); - if (item->info.server_lang >= NETWORK_NUM_LANGUAGES) item->info.server_lang = 0; - if (item->info.map_set >= NUM_LANDSCAPE ) item->info.map_set = 0; + str_validate(item->info.server_name); + str_validate(item->info.server_revision); + str_validate(item->info.map_name); + if (item->info.server_lang >= NETWORK_NUM_LANGUAGES) item->info.server_lang = 0; + if (item->info.map_set >= NUM_LANDSCAPE ) item->info.map_set = 0; - if (item->info.hostname[0] == '\0') - snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr)); + if (item->info.hostname[0] == '\0') + snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr)); + break; } item->online = true; diff --git a/players.c b/players.c --- a/players.c +++ b/players.c @@ -856,13 +856,15 @@ int32 CmdPlayerCtrl(int x, int y, uint32 _local_player = ci->client_playas - 1; NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL); _local_player = player_backup; + _network_game_info.companies_on++; } } - } else if (_network_server) { + } else if (_network_server) { // Creating player failed, defer client to spectator /* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at server-side * in network_server.c:838, function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */ NetworkClientInfo *ci = &_network_client_info[pid]; ci->client_playas = OWNER_SPECTATOR; + _network_game_info.spectators_on++; NetworkUpdateClientInfo(ci->client_index); } #else @@ -901,6 +903,9 @@ int32 CmdPlayerCtrl(int x, int y, uint32 p->is_active = false; } RemoveAllEngineReplacementForPlayer(p); +#ifdef ENABLE_NETWORK + _network_game_info.companies_on--; +#endif /* ENABLE_NETWORK */ } break;