From 02b3bc57d5cf6336343fd9dda09469221275bb35 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sun, 5 Dec 2010 14:45:52 +0000 Subject: (svn r21398) -Change/Feature-ish: when making a savegame to send to a client, don't write it to disk but create the packets immediately --- src/network/network_server.cpp | 130 +++++++++++++++++++++++++++++------------ 1 file changed, 93 insertions(+), 37 deletions(-) (limited to 'src/network/network_server.cpp') diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 40c5c2f26..6c65aaced 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -24,6 +24,7 @@ #include "../company_base.h" #include "../command_func.h" #include "../saveload/saveload.h" +#include "../saveload/saveload_filter.h" #include "../station_base.h" #include "../genworld.h" #include "../fileio_func.h" @@ -54,6 +55,82 @@ INSTANTIATE_POOL_METHODS(NetworkClientSocket) /** Instantiate the listen sockets. */ template SocketList TCPListenHandler::sockets; +/** Writing a savegame directly to a number of packets. */ +struct PacketWriter : SaveFilter { + ServerNetworkGameSocketHandler *cs; ///< Socket we are associated with. + Packet *current; ///< The packet we're currently writing to. + size_t total_size; ///< Total size of the compressed savegame. + + /** + * Create the packet writer. + * @param cs The socket handler we're making the packets for. + */ + PacketWriter(ServerNetworkGameSocketHandler *cs) : SaveFilter(NULL), cs(cs), current(NULL), total_size(0) + { + } + + /** Make sure everything is cleaned up. */ + ~PacketWriter() + { + /* Prevent double frees. */ + this->cs->savegame = NULL; + + delete this->current; + } + + /** Append the current packet to the queue. */ + void AppendQueue() + { + if (this->current == NULL) return; + + Packet **p = &this->cs->savegame_packets; + while (*p != NULL) { + p = &(*p)->next; + } + *p = this->current; + + this->current = NULL; + } + + /* virtual */ void Write(byte *buf, size_t size) + { + if (cs == NULL) return; + + if (this->current == NULL) this->current = new Packet(PACKET_SERVER_MAP_DATA); + + byte *bufe = buf + size; + while (buf != bufe) { + size_t to_write = min(SEND_MTU - this->current->size, bufe - buf); + memcpy(this->current->buffer + this->current->size, buf, to_write); + this->current->size += to_write; + buf += to_write; + + if (this->current->size == SEND_MTU) { + this->AppendQueue(); + if (buf != bufe) this->current = new Packet(PACKET_SERVER_MAP_DATA); + } + } + + this->total_size += size; + } + + /* virtual */ void Finish() + { + /* Make sure the last packet is flushed. */ + this->AppendQueue(); + + /* Add a packet stating that this is the end to the queue. */ + this->current = new Packet(PACKET_SERVER_MAP_DONE); + this->AppendQueue(); + + /* Fast-track the size to the client. */ + Packet *p = new Packet(PACKET_SERVER_MAP_SIZE); + p->Send_uint32(this->total_size); + this->cs->SendPacket(p); + } +}; + + /** * Create a new socket for the server side of the game connection. * @param s The socket to connect with. @@ -76,6 +153,7 @@ ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler() { if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID; OrderBackup::ResetUser(this->client_id); + delete this->savegame_packets; } Packet *ServerNetworkGameSocketHandler::ReceivePacket() @@ -377,7 +455,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait() /* This sends the map to the client */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() { - static FILE *file_pointer = NULL; static uint sent_packets; // How many packets we did send succecfully last time if (this->status < STATUS_AUTHORIZED) { @@ -386,62 +463,41 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() } if (this->status == STATUS_AUTHORIZED) { - char filename[MAX_PATH]; - FioGetDirectory(filename, lengthof(filename), AUTOSAVE_DIR); - strecat(filename, "network_server.tmp", lastof(filename)); - - /* Make a dump of the current game */ - if (SaveOrLoad(filename, SL_SAVE, NO_DIRECTORY) != SL_OK) usererror("network savedump failed"); - - if (file_pointer != NULL) fclose(file_pointer); - - file_pointer = FioFOpenFile(filename, "rb", NO_DIRECTORY); - if (file_pointer == NULL) usererror("network savedump failed - could not open just saved dump"); - - fseek(file_pointer, 0, SEEK_END); - if (ftell(file_pointer) == 0) usererror("network savedump failed - zero sized savegame?"); + this->savegame = new PacketWriter(this); /* Now send the _frame_counter and how many packets are coming */ Packet *p = new Packet(PACKET_SERVER_MAP_BEGIN); p->Send_uint32(_frame_counter); this->SendPacket(p); - p = new Packet(PACKET_SERVER_MAP_SIZE); - p->Send_uint32(ftell(file_pointer)); - this->SendPacket(p); - - fseek(file_pointer, 0, SEEK_SET); - - sent_packets = 4; // We start with trying 4 packets - NetworkSyncCommandQueue(this); this->status = STATUS_MAP; /* Mark the start of download */ this->last_frame = _frame_counter; this->last_frame_server = _frame_counter; + + sent_packets = 4; // We start with trying 4 packets + + /* Make a dump of the current game */ + if (SaveWithFilter(this->savegame, false) != SL_OK) usererror("network savedump failed"); } if (this->status == STATUS_MAP) { - uint i; - int res; - for (i = 0; i < sent_packets; i++) { - Packet *p = new Packet(PACKET_SERVER_MAP_DATA); - res = (int)fread(p->buffer + p->size, 1, SEND_MTU - p->size, file_pointer); + for (uint i = 0; i < sent_packets && this->savegame_packets != NULL; i++) { + Packet *p = this->savegame_packets; + bool last_packet = p->buffer[2] == PACKET_SERVER_MAP_DONE; - if (ferror(file_pointer)) usererror("Error reading temporary network savegame!"); - - p->size += res; + /* Remove the packet from the savegame queue and put it in the real queue. */ + this->savegame_packets = p->next; + p->next = NULL; this->SendPacket(p); - if (feof(file_pointer)) { + + if (last_packet) { /* Done reading! */ - Packet *p = new Packet(PACKET_SERVER_MAP_DONE); - this->SendPacket(p); /* Set the status to DONE_MAP, no we will wait for the client * to send it is ready (maybe that happens like never ;)) */ this->status = STATUS_DONE_MAP; - fclose(file_pointer); - file_pointer = NULL; NetworkClientSocket *new_cs; bool new_map_client = false; @@ -470,7 +526,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() /* Send all packets (forced) and check if we have send it all */ if (this->SendPackets() && this->IsPacketQueueEmpty()) { /* All are sent, increase the sent_packets */ - sent_packets *= 2; + if (this->savegame_packets != NULL) sent_packets *= 2; } else { /* Not everything is sent, decrease the sent_packets */ if (sent_packets > 1) sent_packets /= 2; -- cgit v1.2.3-54-g00ecf