diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -628,7 +628,7 @@ > next; - free(p); - } - _networking = false; _network_server = false; @@ -639,8 +630,6 @@ static void NetworkClose() // Inits the network (cleans sockets and stuff) static void NetworkInitialize() { - _local_command_queue = NULL; - _NetworkClientSocket_pool.CleanPool(); _NetworkClientSocket_pool.AddBlockToPool(); _NetworkClientInfo_pool.CleanPool(); @@ -912,46 +901,11 @@ static void NetworkSend() } } -// Handle the local-command-queue -static void NetworkHandleLocalQueue() -{ - CommandPacket *cp, **cp_prev; - - cp_prev = &_local_command_queue; - - while ( (cp = *cp_prev) != NULL) { - - // The queue is always in order, which means - // that the first element will be executed first. - if (_frame_counter < cp->frame) break; - - if (_frame_counter > cp->frame) { - // If we reach here, it means for whatever reason, we've already executed - // past the command we need to execute. - error("[net] Trying to execute a packet in the past!"); - } - - // We can execute this command - NetworkExecuteCommand(cp); - - *cp_prev = cp->next; - free(cp); - } - - // Just a safety check, to be removed in the future. - // Make sure that no older command appears towards the end of the queue - // In that case we missed executing it. This will never happen. - for (cp = _local_command_queue; cp; cp = cp->next) { - assert(_frame_counter < cp->frame); - } - -} - static bool NetworkDoClientLoop() { _frame_counter++; - NetworkHandleLocalQueue(); + NetworkExecuteLocalCommandQueue(); StateGameLoop(); @@ -1050,7 +1004,7 @@ void NetworkGameLoop() send_frame = true; } - NetworkHandleLocalQueue(); + NetworkExecuteLocalCommandQueue(); // Then we make the frame StateGameLoop(); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -673,47 +673,36 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND) { - CommandPacket *cp = MallocT(1); - cp->company = (CompanyID)p->Recv_uint8(); - cp->cmd = p->Recv_uint32(); - cp->p1 = p->Recv_uint32(); - cp->p2 = p->Recv_uint32(); - cp->tile = p->Recv_uint32(); - p->Recv_string(cp->text, sizeof(cp->text)); - cp->callback = p->Recv_uint8(); - cp->frame = p->Recv_uint32(); - cp->my_cmd = p->Recv_bool(); - cp->next = NULL; + CommandPacket cp; + cp.company = (CompanyID)p->Recv_uint8(); + cp.cmd = p->Recv_uint32(); + cp.p1 = p->Recv_uint32(); + cp.p2 = p->Recv_uint32(); + cp.tile = p->Recv_uint32(); + p->Recv_string(cp.text, sizeof(cp.text)); + cp.callback = p->Recv_uint8(); + cp.frame = p->Recv_uint32(); + cp.my_cmd = p->Recv_bool(); + cp.next = NULL; - if (!IsValidCommand(cp->cmd)) { + if (!IsValidCommand(cp.cmd)) { IConsolePrintF(CC_ERROR, "WARNING: invalid command from server, dropping..."); - free(cp); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } - if (GetCommandFlags(cp->cmd) & CMD_OFFLINE) { + if (GetCommandFlags(cp.cmd) & CMD_OFFLINE) { IConsolePrintF(CC_ERROR, "WARNING: offline only command from server, dropping..."); - free(cp); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } - if ((cp->cmd & CMD_FLAGS_MASK) != 0) { + if ((cp.cmd & CMD_FLAGS_MASK) != 0) { IConsolePrintF(CC_ERROR, "WARNING: invalid command flag from server, dropping..."); - free(cp); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } // The server did send us this command.. // queue it in our own queue, so we can handle it in the upcoming frame! - - if (_local_command_queue == NULL) { - _local_command_queue = cp; - } else { - // Find last packet - CommandPacket *c = _local_command_queue; - while (c->next != NULL) c = c->next; - c->next = cp; - } + NetworkAddCommandQueue(cp); return NETWORK_RECV_STATUS_OKAY; } diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp new file mode 100644 --- /dev/null +++ b/src/network/network_command.cpp @@ -0,0 +1,158 @@ +/* $Id$ */ + +/** @file network_data.cpp Command handling over network connections. */ + +#ifdef ENABLE_NETWORK + +#include "../stdafx.h" +#include "../debug.h" +#include "network_internal.h" +#include "network_client.h" +#include "../command_func.h" +#include "../callback_table.h" +#include "../core/alloc_func.hpp" +#include "../string_func.h" +#include "../date_func.h" +#include "../company_func.h" + +/** Local queue of packets */ +static CommandPacket *_local_command_queue = NULL; + +/** + * Add a command to the local or client socket command queue, + * based on the socket. + * @param cp the command packet to add + * @param cs the socket to send to (NULL = locally) + */ +void NetworkAddCommandQueue(CommandPacket cp, NetworkClientSocket *cs) +{ + CommandPacket *new_cp = MallocT(1); + *new_cp = cp; + + CommandPacket **begin = (cs == NULL ? &_local_command_queue : &cs->command_queue); + + if (*begin == NULL) { + *begin = new_cp; + } else { + CommandPacket *c = *begin; + while (c->next != NULL) c = c->next; + c->next = new_cp; + } +} + +/** + * Prepare a DoCommand to be send over the network + * @param tile The tile to perform a command on (see #CommandProc) + * @param p1 Additional data for the command (see #CommandProc) + * @param p2 Additional data for the command (see #CommandProc) + * @param cmd The command to execute (a CMD_* value) + * @param callback A callback function to call after the command is finished + * @param text The text to pass + */ +void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text) +{ + assert((cmd & CMD_FLAGS_MASK) == 0); + + CommandPacket c; + c.company = _local_company; + c.next = NULL; + c.tile = tile; + c.p1 = p1; + c.p2 = p2; + c.cmd = cmd; + + c.callback = 0; + while (c.callback < _callback_table_count && _callback_table[c.callback] != callback) { + c.callback++; + } + + if (c.callback == _callback_table_count) { + DEBUG(net, 0, "Unknown callback. (Pointer: %p) No callback sent", callback); + c.callback = 0; // _callback_table[0] == NULL + } + + strecpy(c.text, (text != NULL) ? text : "", lastof(c.text)); + + if (_network_server) { + /* If we are the server, we queue the command in our 'special' queue. + * In theory, we could execute the command right away, but then the + * client on the server can do everything 1 tick faster than others. + * So to keep the game fair, we delay the command with 1 tick + * which gives about the same speed as most clients. + */ + c.frame = _frame_counter_max + 1; + + NetworkAddCommandQueue(c); + + /* Only the local client (in this case, the server) gets the callback */ + c.callback = 0; + /* And we queue it for delivery to the clients */ + NetworkClientSocket *cs; + FOR_ALL_CLIENT_SOCKETS(cs) { + if (cs->status > STATUS_MAP_WAIT) NetworkAddCommandQueue(c, cs); + } + return; + } + + c.frame = 0; // The client can't tell which frame, so just make it 0 + + /* Clients send their command to the server and forget all about the packet */ + SEND_COMMAND(PACKET_CLIENT_COMMAND)(&c); +} + +/** + * Execute a DoCommand we received from the network + * @param cp the command to execute + */ +static void NetworkExecuteCommand(CommandPacket *cp) +{ + _current_company = cp->company; + /* cp->callback is unsigned. so we don't need to do lower bounds checking. */ + if (cp->callback > _callback_table_count) { + DEBUG(net, 0, "Received out-of-bounds callback (%d)", cp->callback); + cp->callback = 0; + } + + DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd | CMD_NETWORK_COMMAND, _callback_table[cp->callback], cp->text, cp->my_cmd); +} + +/** + * Execute all commands on the local command queue that ought to be executed this frame. + */ +void NetworkExecuteLocalCommandQueue() +{ + while (_local_command_queue != NULL) { + + /* The queue is always in order, which means + * that the first element will be executed first. */ + if (_frame_counter < _local_command_queue->frame) break; + + if (_frame_counter > _local_command_queue->frame) { + /* If we reach here, it means for whatever reason, we've already executed + * past the command we need to execute. */ + error("[net] Trying to execute a packet in the past!"); + } + + /* We can execute this command */ + NetworkExecuteCommand(_local_command_queue); + + CommandPacket *cp = _local_command_queue; + _local_command_queue = _local_command_queue->next; + free(cp); + } +} + +/** + * Free the local command queue. + */ +void NetworkFreeLocalCommandQueue() +{ + /* Free all queued commands */ + while (_local_command_queue != NULL) { + CommandPacket *p = _local_command_queue; + _local_command_queue = _local_command_queue->next; + free(p); + } +} + +#endif /* ENABLE_NETWORK */ diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp deleted file mode 100644 --- a/src/network/network_data.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* $Id$ */ - -/** @file network_data.cpp Command handling over network connections. */ - -#ifdef ENABLE_NETWORK - -#include "../stdafx.h" -#include "../debug.h" -#include "network_internal.h" -#include "network_client.h" -#include "../command_func.h" -#include "../callback_table.h" -#include "../core/alloc_func.hpp" -#include "../string_func.h" -#include "../date_func.h" -#include "../company_func.h" - -// Add a command to the local command queue -void NetworkAddCommandQueue(NetworkClientSocket *cs, CommandPacket *cp) -{ - CommandPacket* new_cp = MallocT(1); - - *new_cp = *cp; - - if (cs->command_queue == NULL) { - cs->command_queue = new_cp; - } else { - CommandPacket *c = cs->command_queue; - while (c->next != NULL) c = c->next; - c->next = new_cp; - } -} - -// Prepare a DoCommand to be send over the network -void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text) -{ - assert((cmd & CMD_FLAGS_MASK) == 0); - - CommandPacket c; - - c.company = _local_company; - c.next = NULL; - c.tile = tile; - c.p1 = p1; - c.p2 = p2; - c.cmd = cmd; - - c.callback = 0; - while (c.callback < _callback_table_count && _callback_table[c.callback] != callback) { - c.callback++; - } - - if (c.callback == _callback_table_count) { - DEBUG(net, 0, "Unknown callback. (Pointer: %p) No callback sent", callback); - c.callback = 0; // _callback_table[0] == NULL - } - - strecpy(c.text, (text != NULL) ? text : "", lastof(c.text)); - - if (_network_server) { - /* If we are the server, we queue the command in our 'special' queue. - * In theory, we could execute the command right away, but then the - * client on the server can do everything 1 tick faster than others. - * So to keep the game fair, we delay the command with 1 tick - * which gives about the same speed as most clients. - */ - c.frame = _frame_counter_max + 1; - - CommandPacket *new_cp = MallocT(1); - *new_cp = c; - new_cp->my_cmd = true; - if (_local_command_queue == NULL) { - _local_command_queue = new_cp; - } else { - /* Find last packet */ - CommandPacket *cp = _local_command_queue; - while (cp->next != NULL) cp = cp->next; - cp->next = new_cp; - } - - /* Only the local client (in this case, the server) gets the callback */ - c.callback = 0; - /* And we queue it for delivery to the clients */ - NetworkClientSocket *cs; - FOR_ALL_CLIENT_SOCKETS(cs) { - if (cs->status > STATUS_MAP_WAIT) NetworkAddCommandQueue(cs, &c); - } - return; - } - - c.frame = 0; // The client can't tell which frame, so just make it 0 - - /* Clients send their command to the server and forget all about the packet */ - SEND_COMMAND(PACKET_CLIENT_COMMAND)(&c); -} - -// Execute a DoCommand we received from the network -void NetworkExecuteCommand(CommandPacket *cp) -{ - _current_company = cp->company; - /* cp->callback is unsigned. so we don't need to do lower bounds checking. */ - if (cp->callback > _callback_table_count) { - DEBUG(net, 0, "Received out-of-bounds callback (%d)", cp->callback); - cp->callback = 0; - } - - DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd | CMD_NETWORK_COMMAND, _callback_table[cp->callback], cp->text, cp->my_cmd); -} - -#endif /* ENABLE_NETWORK */ diff --git a/src/network/network_internal.h b/src/network/network_internal.h --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -94,6 +94,7 @@ enum NetworkLanguage { extern uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode extern uint32 _frame_counter_max; // To where we may go with our clients +extern uint32 _frame_counter; extern uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients. @@ -119,9 +120,6 @@ extern uint16 _network_udp_broadcast; extern uint8 _network_advertise_retries; -// following externs are instantiated at network.cpp -extern CommandPacket *_local_command_queue; - void NetworkTCPQueryServer(const char* host, unsigned short port); void NetworkAddServer(const char *b); @@ -130,8 +128,10 @@ void UpdateNetworkGameWindow(bool unsele bool IsNetworkCompatibleVersion(const char *version); -void NetworkExecuteCommand(CommandPacket *cp); -void NetworkAddCommandQueue(NetworkClientSocket *cs, CommandPacket *cp); +/* From network_command.cpp */ +void NetworkAddCommandQueue(CommandPacket cp, NetworkClientSocket *cs = NULL); +void NetworkExecuteLocalCommandQueue(); +void NetworkFreeLocalCommandQueue(); // from network.c void NetworkCloseClient(NetworkClientSocket *cs); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -817,12 +817,12 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT /** Enforce the command flags. * Eg a server-only command can only be executed by a server, etc. - * @param *cp the commandpacket that is going to be checked - * @param *ci client information for debugging output to console + * @param cp the commandpacket that is going to be checked + * @param ci client information for debugging output to console */ -static bool CheckCommandFlags(const CommandPacket *cp, const NetworkClientInfo *ci) +static bool CheckCommandFlags(CommandPacket cp, const NetworkClientInfo *ci) { - byte flags = GetCommandFlags(cp->cmd); + byte flags = GetCommandFlags(cp.cmd); if (flags & CMD_SERVER && ci->client_id != CLIENT_ID_SERVER) { IConsolePrintF(CC_ERROR, "WARNING: server only command from client %d (IP: %s), kicking...", ci->client_id, GetClientIP(ci)); @@ -834,12 +834,12 @@ static bool CheckCommandFlags(const Comm return false; } - if (cp->cmd != CMD_COMPANY_CTRL && !IsValidCompanyID(cp->company) && ci->client_id != CLIENT_ID_SERVER) { + if (cp.cmd != CMD_COMPANY_CTRL && !IsValidCompanyID(cp.company) && ci->client_id != CLIENT_ID_SERVER) { IConsolePrintF(CC_ERROR, "WARNING: spectator issueing command from client %d (IP: %s), kicking...", ci->client_id, GetClientIP(ci)); return false; } - if ((cp->cmd & CMD_FLAGS_MASK) != 0) { + if ((cp.cmd & CMD_FLAGS_MASK) != 0) { IConsolePrintF(CC_ERROR, "WARNING: invalid command flag from client %d (IP: %s), kicking...", ci->client_id, GetClientIP(ci)); return false; } @@ -854,8 +854,6 @@ static bool CheckCommandFlags(const Comm DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) { NetworkClientSocket *new_cs; - const NetworkClientInfo *ci; - byte callback; // The client was never joined.. so this is impossible, right? // Ignore the packet, give the client a warning, and close his connection @@ -864,34 +862,29 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT return; } - CommandPacket *cp = MallocT(1); - cp->company = (CompanyID)p->Recv_uint8(); - cp->cmd = p->Recv_uint32(); - cp->p1 = p->Recv_uint32(); - cp->p2 = p->Recv_uint32(); - cp->tile = p->Recv_uint32(); - p->Recv_string(cp->text, lengthof(cp->text)); + CommandPacket cp; + cp.company = (CompanyID)p->Recv_uint8(); + cp.cmd = p->Recv_uint32(); + cp.p1 = p->Recv_uint32(); + cp.p2 = p->Recv_uint32(); + cp.tile = p->Recv_uint32(); + p->Recv_string(cp.text, lengthof(cp.text)); - callback = p->Recv_uint8(); + byte callback = p->Recv_uint8(); - if (cs->has_quit) { - free(cp); - return; - } + if (cs->has_quit) return; - ci = cs->GetInfo(); + const NetworkClientInfo *ci = cs->GetInfo(); /* Check if cp->cmd is valid */ - if (!IsValidCommand(cp->cmd)) { + if (!IsValidCommand(cp.cmd)) { IConsolePrintF(CC_ERROR, "WARNING: invalid command from client %d (IP: %s).", ci->client_id, GetClientIP(ci)); SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); - free(cp); return; } if (!CheckCommandFlags(cp, ci)) { SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_KICKED); - free(cp); return; } @@ -899,11 +892,10 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT * to match the company in the packet. If it doesn't, the client has done * something pretty naughty (or a bug), and will be kicked */ - if (!(cp->cmd == CMD_COMPANY_CTRL && cp->p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp->company) { + if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) { IConsolePrintF(CC_ERROR, "WARNING: client %d (IP: %s) tried to execute a command as company %d, kicking...", - ci->client_playas + 1, GetClientIP(ci), cp->company + 1); + ci->client_playas + 1, GetClientIP(ci), cp.company + 1); SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_COMPANY_MISMATCH); - free(cp); return; } @@ -912,24 +904,23 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT * is prohibited. Pretty ugly and should be redone together with its function. * @see CmdCompanyCtrl() */ - if (cp->cmd == CMD_COMPANY_CTRL) { - if (cp->p1 != 0) { + if (cp.cmd == CMD_COMPANY_CTRL) { + if (cp.p1 != 0) { SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER); - free(cp); return; } /* XXX - Execute the command as a valid company. Normally this would be done by a * spectator, but that is not allowed any commands. So do an impersonation. The drawback * of this is that the first company's last_built_tile is also updated... */ - cp->company = OWNER_BEGIN; - cp->p2 = cs->client_id; + cp.company = OWNER_BEGIN; + cp.p2 = cs->client_id; } // The frame can be executed in the same frame as the next frame-packet // That frame just before that frame is saved in _frame_counter_max - cp->frame = _frame_counter_max + 1; - cp->next = NULL; + cp.frame = _frame_counter_max + 1; + cp.next = NULL; // Queue the command for the clients (are send at the end of the frame // if they can handle it ;)) @@ -937,23 +928,15 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT if (new_cs->status >= STATUS_MAP) { // Callbacks are only send back to the client who sent them in the // first place. This filters that out. - cp->callback = (new_cs != cs) ? 0 : callback; - cp->my_cmd = (new_cs == cs); - NetworkAddCommandQueue(new_cs, cp); + cp.callback = (new_cs != cs) ? 0 : callback; + cp.my_cmd = (new_cs == cs); + NetworkAddCommandQueue(cp, new_cs); } } - cp->callback = 0; - cp->my_cmd = false; - // Queue the command on the server - if (_local_command_queue == NULL) { - _local_command_queue = cp; - } else { - // Find last packet - CommandPacket *c = _local_command_queue; - while (c->next != NULL) c = c->next; - c->next = cp; - } + cp.callback = 0; + cp.my_cmd = false; + NetworkAddCommandQueue(cp); } DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ERROR) diff --git a/src/variables.h b/src/variables.h --- a/src/variables.h +++ b/src/variables.h @@ -36,8 +36,6 @@ VARDEF byte _trees_tick_ctr; /* NOSAVE: Used in palette animations only, not really important. */ VARDEF int _palette_animation_counter; - -VARDEF uint32 _frame_counter; VARDEF uint32 _realtime_tick; VARDEF bool _is_old_ai_company; // current company is an oldAI company? (enables a lot of cheats..)