Changeset - r7187:ab6c70e149d9
[Not reviewed]
master
0 5 0
rubidium - 17 years ago 2007-07-07 10:06:10
rubidium@openttd.org
(svn r10462) -Add: a command dumper/loader that could be enabled compile-time and server side only to aid debugging some desyncs, i.e. dump the stream of commands so it could be replayed in exactly the same way later. This should primarily be used to make desyncs more easily reproducable, so it can be properly debugged.
5 files changed with 80 insertions and 0 deletions:
0 comments (0 inline, 0 general)
src/date.cpp
Show inline comments
 
@@ -10,12 +10,15 @@
 
#include "vehicle.h"
 
#include "network/network.h"
 
#include "network/network_data.h"
 
#include "network/network_server.h"
 
#include "functions.h"
 
#include "currency.h"
 
#ifdef DEBUG_DUMP_COMMANDS
 
#include "saveload.h"
 
#endif
 

	
 
Year      _cur_year;
 
Month     _cur_month;
 
Date      _date;
 
DateFract _date_fract;
 

	
 
@@ -256,12 +259,18 @@ void IncreaseDate()
 
	ConvertDateToYMD(_date, &ymd);
 
	if (ymd.month == _cur_month) return;
 
	_cur_month = ymd.month;
 

	
 
	/* yes, call various monthly loops */
 
	if (_game_mode != GM_MENU) {
 
#ifdef DEBUG_DUMP_COMMANDS
 
		char name[MAX_PATH];
 
		snprintf(name, lengthof(name), "dmp_cmds_%d.sav", _date);
 
		SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR);
 
		debug_dump_commands("ddc:save:%s\n", name);
 
#endif /* DUMP_COMMANDS */
 
		if (_opt.autosave != 0 && (_cur_month % _autosave_months[_opt.autosave]) == 0) {
 
			_do_autosave = true;
 
			RedrawAutosave();
 
		}
 

	
 
		PlayersMonthlyLoop();
src/network/network.cpp
Show inline comments
 
@@ -34,12 +34,13 @@
 
#include "core/tcp.h"
 
#include "core/core.h"
 
#include "network_gui.h"
 
#include "../console.h" /* IConsoleCmdExec */
 
#include <stdarg.h> /* va_list */
 
#include "../md5.h"
 
#include "../fileio.h"
 

	
 
/* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */
 
assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
 

	
 
// global variables (declared in network_data.h)
 
CommandPacket *_local_command_queue;
 
@@ -192,12 +193,15 @@ void CDECL NetworkTextMessage(NetworkAct
 
			SetDParamStr(1, buf);
 
			GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
	}
 

	
 
#ifdef DEBUG_DUMP_COMMANDS
 
	debug_dump_commands("ddc:cmsg:%d;%d;%s\n", _date, _date_fract, message);
 
#endif /* DUMP_COMMANDS */
 
	IConsolePrintF(color, "%s", message);
 
	AddTextMessage(color, duration, "%s", message);
 
}
 

	
 
// Calculate the frame-lag of a client
 
uint NetworkCalculateLag(const NetworkTCPSocketHandler *cs)
 
@@ -1232,12 +1236,15 @@ static bool NetworkDoClientLoop()
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
			if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) {
 
#else
 
			if (_sync_seed_1 != _random_seeds[0][0]) {
 
#endif
 
				NetworkError(STR_NETWORK_ERR_DESYNC);
 
#ifdef DEBUG_DUMP_COMMANDS
 
				debug_dump_commands("ddc:serr:%d;%d\n", _date, _date_fract);
 
#endif /* DUMP_COMMANDS */
 
				DEBUG(net, 0, "Sync error detected!");
 
				NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
 
				return false;
 
			}
 

	
 
			// If this is the first time we have a sync-frame, we
 
@@ -1277,12 +1284,43 @@ void NetworkGameLoop()
 
{
 
	if (!_networking) return;
 

	
 
	if (!NetworkReceive()) return;
 

	
 
	if (_network_server) {
 
#ifdef DEBUG_DUMP_COMMANDS
 
		static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
 
		static Date next_date = 0;
 
		static uint32 next_date_fract;
 
		static CommandPacket *cp = NULL;
 
		if (f == NULL && next_date == 0) {
 
			printf("Cannot open commands.log\n");
 
			next_date = 1;
 
		}
 

	
 
		while (f != NULL && !feof(f)) {
 
			if (cp != NULL && _date == next_date && _date_fract == next_date_fract) {
 
				_current_player = cp->player;
 
				_cmd_text = cp->text;
 
				DoCommandP(cp->tile, cp->p1, cp->p2, NULL, cp->cmd);
 
				free(cp);
 
				cp = NULL;
 
			}
 

	
 
			if (cp != NULL) break;
 

	
 
			char buff[4096];
 
			if (fgets(buff, lengthof(buff), f) == NULL) break;
 
			if (strncmp(buff, "ddc:cmd:", 8) != 0) continue;
 
			cp = MallocT<CommandPacket>(1);
 
			int player;
 
			sscanf(&buff[8], "%d;%d;%d;%d;%d;%d;%d;%s", &next_date, &next_date_fract, &player, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text);
 
			cp->player = (Owner)player;
 
		}
 
#endif /* DUMP_COMMANDS */
 

	
 
		bool send_frame = false;
 

	
 
		// We first increase the _frame_counter
 
		_frame_counter++;
 
		// Update max-frame-counter
 
		if (_frame_counter > _frame_counter_max) {
 
@@ -1433,7 +1471,21 @@ bool IsNetworkCompatibleVersion(const ch
 
{
 
	extern const char _openttd_revision[];
 
	return strncmp(NOREV_STRING, other, NETWORK_REVISION_LENGTH - 1) == 0 ||
 
			strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
 
}
 

	
 
#ifdef DEBUG_DUMP_COMMANDS
 
void CDECL debug_dump_commands(const char *s, ...)
 
{
 
	static FILE *f = FioFOpenFile("commands-out.log", "wb", SAVE_DIR);
 
	if (f == NULL) return;
 

	
 
	va_list va;
 
	va_start(va, s);
 
	vfprintf(f, s, va);
 
	va_end(va);
 

	
 
	fflush(f);
 
}
 
#endif /* DEBUG_DUMP_COMMANDS */
 
#endif /* ENABLE_NETWORK */
src/network/network.h
Show inline comments
 
@@ -17,12 +17,23 @@
 
//  game.
 
// Remember: both client and server have to be compiled with this
 
//  option enabled to make it to work. If one of the two has it disabled
 
//  nothing will happen.
 
//#define ENABLE_NETWORK_SYNC_EVERY_FRAME
 

	
 
/*
 
 * Dumps all commands that are sent/received to stderr and saves every month.
 
 * This log can become quite large over time; say in the order of two to three
 
 * times the bandwidth used for network games.
 
 */
 
//#define DEBUG_DUMP_COMMANDS
 

	
 
#ifdef DEBUG_DUMP_COMMANDS
 
void CDECL debug_dump_commands(const char *s, ...);
 
#endif /* DEBUG_DUMP_COMMANDS */
 

	
 
// In theory sending 1 of the 2 seeds is enough to check for desyncs
 
//   so in theory, this next define can be left off.
 
//#define NETWORK_SEND_DOUBLE_SEED
 

	
 
// How many clients can we have? Like.. MAX_PLAYERS - 1 is the amount of
 
//  players that can really play.. so.. a max of 4 spectators.. gives us..
src/network/network_data.cpp
Show inline comments
 
@@ -94,10 +94,15 @@ void NetworkExecuteCommand(CommandPacket
 
	_cmd_text = cp->text;
 
	/* 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;
 
	}
 

	
 
#ifdef DEBUG_DUMP_COMMANDS
 
	debug_dump_commands("ddc:cmd:%d;%d;%d;%d;%d;%d;%d;%s\n", _date, _date_fract, (int)cp->player, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text);
 
#endif /* DUMP_COMMANDS */
 

	
 
	DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/saveload.cpp
Show inline comments
 
@@ -1674,12 +1674,15 @@ SaveOrLoadResult SaveOrLoad(const char *
 
			SaveFileDone();
 

	
 
			return result;
 
		}
 
	} else { /* LOAD game */
 
		assert(mode == SL_LOAD);
 
#ifdef DEBUG_DUMP_COMMANDS
 
		debug_dump_commands("ddc:load:%s\n", filename);
 
#endif /* DUMP_COMMANDS */
 

	
 
		if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 

	
 
		/* see if we have any loader for this type. */
 
		for (fmt = _saveload_formats; ; fmt++) {
 
			/* No loader found, treat as version 0 and use LZO format */
0 comments (0 inline, 0 general)