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..)