summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2010-12-05 14:45:52 +0000
committerrubidium <rubidium@openttd.org>2010-12-05 14:45:52 +0000
commit02b3bc57d5cf6336343fd9dda09469221275bb35 (patch)
treee4da6130b31032bf7679eb6aef2a34f723a669ad /src
parent948cffc930eafe9cbc392c478a379c76dca1286b (diff)
downloadopenttd-02b3bc57d5cf6336343fd9dda09469221275bb35.tar.xz
(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
Diffstat (limited to 'src')
-rw-r--r--src/network/network_server.cpp130
-rw-r--r--src/network/network_server.h3
2 files changed, 96 insertions, 37 deletions
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<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::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;
diff --git a/src/network/network_server.h b/src/network/network_server.h
index 89d94a3cc..3f6e51526 100644
--- a/src/network/network_server.h
+++ b/src/network/network_server.h
@@ -72,6 +72,9 @@ public:
CommandQueue outgoing_queue; ///< The command-queue awaiting delivery
int receive_limit; ///< Amount of bytes that we can receive at this moment
+ Packet *savegame_packets; ///< Packet queue of the savegame; send these "slowly" to the client.
+ struct PacketWriter *savegame; ///< Writer used to write the savegame.
+
ServerNetworkGameSocketHandler(SOCKET s);
~ServerNetworkGameSocketHandler();