(svn r21397) -Change/Feature-ish: when getting a savegame from the server, just keep it in memory instead of writing it to a file

This commit is contained in:
rubidium 2010-12-05 14:44:31 +00:00
parent 69766049ce
commit 948cffc930
2 changed files with 105 additions and 34 deletions

View File

@ -16,6 +16,7 @@
#include "network_internal.h"
#include "network_gui.h"
#include "../saveload/saveload.h"
#include "../saveload/saveload_filter.h"
#include "../command_func.h"
#include "../console_func.h"
#include "../fileio_func.h"
@ -37,11 +38,92 @@
/* This file handles all the client-commands */
/** Read some packets, and when do use that data as initial load filter. */
struct PacketReader : LoadFilter {
static const size_t CHUNK = 32 * 1024; ///< 32 KiB chunks of memory.
AutoFreeSmallVector<byte *, 16> blocks; ///< Buffer with blocks of allocated memory.
byte *buf; ///< Buffer we're going to write to/read from.
byte *bufe; ///< End of the buffer we write to/read from.
byte **block; ///< The block we're reading from/writing to.
size_t written_bytes; ///< The total number of bytes we've written.
size_t read_bytes; ///< The total number of read bytes.
/** Initialise everything. */
PacketReader() : LoadFilter(NULL), buf(NULL), bufe(NULL), block(NULL), written_bytes(0), read_bytes(0)
{
}
/**
* Add a packet to this buffer.
* @param p The packet to add.
*/
void AddPacket(const Packet *p)
{
assert(this->read_bytes == 0);
size_t in_packet = p->size - p->pos;
size_t to_write = min(this->bufe - this->buf, in_packet);
const byte *pbuf = p->buffer + p->pos;
this->written_bytes += in_packet;
if (to_write != 0) {
memcpy(this->buf, pbuf, to_write);
this->buf += to_write;
}
/* Did everything fit in the current chunk, then we're done. */
if (to_write == in_packet) return;
/* Allocate a new chunk and add the remaining data. */
pbuf += to_write;
to_write = in_packet - to_write;
this->buf = *this->blocks.Append() = CallocT<byte>(CHUNK);
this->bufe = this->buf + CHUNK;
memcpy(this->buf, pbuf, to_write);
this->buf += to_write;
}
/* virtual */ size_t Read(byte *rbuf, size_t size)
{
/* Limit the amount to read to whatever we still have. */
size_t ret_size = size = min(this->written_bytes - this->read_bytes, size);
this->read_bytes += ret_size;
const byte *rbufe = rbuf + ret_size;
while (rbuf != rbufe) {
if (this->buf == this->bufe) {
this->buf = *this->block++;
this->bufe = this->buf + CHUNK;
}
size_t to_write = min(this->bufe - this->buf, rbufe - rbuf);
memcpy(rbuf, this->buf, to_write);
rbuf += to_write;
this->buf += to_write;
}
return ret_size;
}
/* virtual */ void Reset()
{
this->read_bytes = 0;
this->block = this->blocks.Begin();
this->buf = *this->block++;
this->bufe = this->buf + CHUNK;
}
};
/**
* Create a new socket for the client side of the game connection.
* @param s The socket to connect with.
*/
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s), download_file(NULL), status(STATUS_INACTIVE)
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s), savegame(NULL), status(STATUS_INACTIVE)
{
assert(ClientNetworkGameSocketHandler::my_client == NULL);
ClientNetworkGameSocketHandler::my_client = this;
@ -53,9 +135,7 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
assert(ClientNetworkGameSocketHandler::my_client == this);
ClientNetworkGameSocketHandler::my_client = NULL;
/* If for whatever reason the handle isn't closed, do it now. */
if (this->download_file != NULL) fclose(this->download_file);
free(this->download_filename);
delete this->savegame;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
@ -691,18 +771,9 @@ DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_BEGIN)
if (this->status < STATUS_AUTHORIZED || this->status >= STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
this->status = STATUS_MAP;
if (this->download_file != NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->savegame != NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
char filename[MAX_PATH];
FioGetDirectory(filename, lengthof(filename), AUTOSAVE_DIR);
strecat(filename, "network_client.tmp", lastof(filename));
this->download_file = FioFOpenFile(filename, "wb", NO_DIRECTORY);
if (this->download_file == NULL) {
_switch_mode_errorstr = STR_NETWORK_ERROR_SAVEGAMEERROR;
return NETWORK_RECV_STATUS_SAVEGAME;
}
this->download_filename = strdup(filename);
this->savegame = new PacketReader();
_frame_counter = _frame_counter_server = _frame_counter_max = p->Recv_uint32();
@ -718,7 +789,7 @@ DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_BEGIN)
DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_SIZE)
{
if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->download_file == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
_network_join_bytes_total = p->Recv_uint32();
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, 0);
@ -729,17 +800,12 @@ DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_SIZE)
DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_DATA)
{
if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->download_file == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
/* We are still receiving data, put it to the file */
if (fwrite(p->buffer + p->pos, 1, p->size - p->pos, this->download_file) != (size_t)(p->size - p->pos)) {
_switch_mode_errorstr = STR_NETWORK_ERROR_SAVEGAMEERROR;
fclose(this->download_file);
this->download_file = NULL;
return NETWORK_RECV_STATUS_SAVEGAME;
}
this->savegame->AddPacket(p);
_network_join_bytes = ftell(this->download_file);
_network_join_bytes = this->savegame->written_bytes;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, 0);
return NETWORK_RECV_STATUS_OKAY;
@ -748,18 +814,24 @@ DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_DATA)
DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_MAP_DONE)
{
if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->download_file == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
fclose(this->download_file);
this->download_file = NULL;
if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, 0);
/*
* Make sure everything is set for reading.
*
* We need the local copy and reset this->savegame because when
* loading fails the network gets reset upon loading the intro
* game, which would cause us to free this->savegame twice.
*/
LoadFilter *lf = this->savegame;
this->savegame = NULL;
lf->Reset();
/* The map is done downloading, load it */
bool load_success = SafeLoad(this->download_filename, SL_LOAD, GM_NORMAL, NO_DIRECTORY);
free(this->download_filename);
this->download_filename = NULL;
bool load_success = SafeLoad(NULL, SL_LOAD, GM_NORMAL, NO_DIRECTORY, lf);
if (!load_success) {
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);

View File

@ -19,9 +19,8 @@
/** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler {
private:
FILE *download_file; ///< Handle used for downloading the savegame.
char *download_filename; ///< File name of the downloading savegame, so we open the right one.
byte token; ///< The token we need to send back to the server to prove we're the right client.
struct PacketReader *savegame; ///< Packet reader for reading the savegame.
byte token; ///< The token we need to send back to the server to prove we're the right client.
/** Status of the connection with the server. */
enum ServerStatus {