Index: src/console_cmds.cpp =================================================================== --- src/console_cmds.cpp (revision 11336) +++ src/console_cmds.cpp (working copy) @@ -668,6 +668,53 @@ return true; } +DEF_CONSOLE_CMD(ConMoveClient) +{ + NetworkClientInfo *ci; + uint16 client_index; + PlayerID player_index; + + if (argc < 2) { + IConsoleHelp("Move a player to another company. Usage: move []"); + IConsoleHelp("For client-id see 'clients', company-id must be between 1 and 8, ommit if moving to spectators"); + return true; + } + + client_index = (uint16)atoi(argv[1]); + player_index = (argc == 3) ? (PlayerID)(atoi(argv[2]) - 1) : PLAYER_SPECTATOR; + + if (client_index == NETWORK_SERVER_INDEX) { + IConsoleError("Silly boy, you can not move the server!"); + return true; + } + + if (client_index == 0) { + IConsoleError("Invalid client"); + return true; + } + + ci = NetworkFindClientInfoFromIndex(client_index); + + if ((!IsValidPlayer(player_index) || player_index > ActivePlayerCount()-1) && player_index != PLAYER_SPECTATOR) { + IConsolePrintF(_icolour_err, "Company does not exist. Company-id must be between 1 and %d.", ActivePlayerCount()); + return true; + } + + if (ci != NULL) { + /* We are good to make the change, after passing all tests. */ + ci->client_playas = player_index; + /* First update the client itself, so that client side windows can be disabled in time. */ + SEND_COMMAND(PACKET_CLIENT_MOVE)(NetworkFindClientStateFromIndex(client_index), client_index, player_index); + /* Now spread the workd to everyone else. */ + NetworkUpdateClientInfo(client_index); + IConsolePrintF(_icolour_err, "[move] client %d to company %d.", client_index, min(player_index+1, PLAYER_SPECTATOR)); + } else { + IConsoleError("Client not found"); + } + + return true; +} + DEF_CONSOLE_CMD(ConResetCompany) { const Player *p; @@ -1565,6 +1612,8 @@ IConsoleCmdRegister("rcon", ConRcon); IConsoleCmdHookAdd("rcon", ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork); + IConsoleCmdRegister("move", ConMoveClient); + IConsoleCmdHookAdd("move", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); IConsoleCmdRegister("reset_company", ConResetCompany); IConsoleCmdHookAdd("reset_company", ICONSOLE_HOOK_ACCESS, ConHookServerOnly); IConsoleAliasRegister("clean_company", "reset_company %A"); Index: src/network/core/tcp.h =================================================================== --- src/network/core/tcp.h (revision 11336) +++ src/network/core/tcp.h (working copy) @@ -54,6 +54,7 @@ PACKET_CLIENT_RCON, PACKET_SERVER_CHECK_NEWGRFS, PACKET_CLIENT_NEWGRFS_CHECKED, + PACKET_CLIENT_MOVE, PACKET_END ///< Must ALWAYS be on the end of this list!! (period) }; Index: src/network/network_server.cpp =================================================================== --- src/network/network_server.cpp (revision 11336) +++ src/network/network_server.cpp (working copy) @@ -590,6 +590,25 @@ cs->Send_Packet(p); } +DEF_SERVER_SEND_COMMAND_PARAM(PACKET_CLIENT_MOVE)(NetworkTCPSocketHandler *cs, uint16 client_index, uint8 player_index) +{ + // + // Packet: PACKET_CLIENT_MOVE + // Function: Move a client from one company to another, or to Spectator. + // Data: + // uint16: Client-index + // uint8: PlayerID + // + + Packet *p = NetworkSend_Init(PACKET_CLIENT_MOVE); + + DEBUG(net, 0, "[move] client %d to player %d", client_index, min(player_index+1, PLAYER_SPECTATOR)); + + p->Send_uint16(client_index); + p->Send_uint8(player_index); + cs->Send_Packet(p); +} + // ********** // Receiving functions // DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkTCPSocketHandler *cs, Packet *p @@ -1239,6 +1258,7 @@ RECEIVE_COMMAND(PACKET_CLIENT_RCON), NULL, /*PACKET_CLIENT_CHECK_NEWGRFS,*/ RECEIVE_COMMAND(PACKET_CLIENT_NEWGRFS_CHECKED), + NULL /*PACKET_CLIENT_MOVE,*/ }; // If this fails, check the array above with network_data.h Index: src/network/network_server.h =================================================================== --- src/network/network_server.h (revision 11336) +++ src/network/network_server.h (working copy) @@ -11,6 +11,7 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN); DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME); DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_RCON)(NetworkTCPSocketHandler *cs, uint16 color, const char *command); +DEF_SERVER_SEND_COMMAND_PARAM(PACKET_CLIENT_MOVE)(NetworkTCPSocketHandler *cs, uint16 client_index, uint8 player_index); bool NetworkFindName(char new_name[NETWORK_CLIENT_NAME_LENGTH]); void NetworkServer_HandleChat(NetworkAction action, DestType type, int dest, const char *msg, uint16 from_index); Index: src/network/network_client.cpp =================================================================== --- src/network/network_client.cpp (revision 11336) +++ src/network/network_client.cpp (working copy) @@ -357,8 +357,12 @@ // Client name changed, display the change NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", name); } else if (playas != ci->client_playas) { - // The player changed from client-player.. - // Do not display that for now + /* Client has been moved to another company or to spectator, show a message. */ + if (playas == PLAYER_SPECTATOR || playas > MAX_PLAYERS) { + NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE, 1, false, ci->client_name, "'%s' Moved To Spectators", ci->client_name); + } else { + NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE, 1, false, ci->client_name, "'%s' Moved To Company #%d", ci->client_name, playas+1); + } } ci->client_playas = playas; @@ -789,6 +793,43 @@ return NETWORK_RECV_STATUS_OKAY; } +DEF_CLIENT_RECEIVE_COMMAND(PACKET_CLIENT_MOVE) +{ + uint16 client_index; + PlayerID player_index; + + /* Nothing more in this packet... */ + client_index = p->Recv_uint16(); + player_index = (Owner)p->Recv_uint8(); + + if (client_index == 0) { + /* deffinately an invalide client id, debug message and do nothing. */ + DEBUG(net, 0, "[move] received client index = 0"); + return NETWORK_RECV_STATUS_OKAY; + } + + const NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(client_index); + /* Just make sure we do not try to use a client_index that does not exist */ + if (ci != NULL) { + if (!IsValidPlayer(player_index) || player_index > ActivePlayerCount()-1) { + player_index = PLAYER_SPECTATOR; + } + + if (client_index == _network_own_client_index) { + /* this packet is actually for this client... apply the new player_index */ + DEBUG(net, 0, "[move] client %d to player %d", client_index, min(player_index+1, PLAYER_SPECTATOR)); + + _network_playas = player_index; + SetLocalPlayer(player_index); + + /* make sure BUILD_MENU is disabled and all other windows are closed */ + UpdateWindows(); + DeleteAllNonVitalWindows(); + } + } + + return NETWORK_RECV_STATUS_OKAY; +} // The layout for the receive-functions by the client @@ -833,6 +874,7 @@ NULL, /*PACKET_CLIENT_RCON,*/ RECEIVE_COMMAND(PACKET_SERVER_CHECK_NEWGRFS), NULL, /*PACKET_CLIENT_NEWGRFS_CHECKED,*/ + RECEIVE_COMMAND(PACKET_CLIENT_MOVE), }; // If this fails, check the array above with network_data.h