From 83cca3a75aa67f22f8f4d637eeb674a8678b9820 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sun, 11 Apr 2010 10:57:21 +0000 Subject: [PATCH] (svn r19603) [1.0] -Backport from trunk: - Fix: Desync debugging; false positives in the cache validity checks and saving/loading the command stream (r19601, r19600, r19596, r19593, r19592, r19589, r19587, r19586) --- src/command.cpp | 236 ++++++++++++++++++--------------- src/command_func.h | 4 + src/command_type.h | 5 +- src/date.cpp | 6 - src/debug.cpp | 2 +- src/genworld.cpp | 2 +- src/network/network.cpp | 63 +++++++-- src/network/network_internal.h | 12 ++ src/network/network_server.cpp | 3 + src/openttd.cpp | 25 +++- src/saveload/saveload.cpp | 4 +- src/video/dedicated_v.cpp | 6 +- 12 files changed, 225 insertions(+), 143 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 430f9d2ebc..241c45faa9 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -188,6 +188,8 @@ CommandProc CmdSetVehicleOnTime; CommandProc CmdAutofillTimetable; CommandProc CmdSetTimetableStart; +#define DEF_CMD(proc, flags) {proc, #proc, flags} + /** * The master command table * @@ -196,141 +198,141 @@ CommandProc CmdSetTimetableStart; * as the value from the CMD_* enums. */ static const Command _command_proc_table[] = { - {CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_RAILROAD_TRACK - {CmdRemoveRailroadTrack, CMD_AUTO}, // CMD_REMOVE_RAILROAD_TRACK - {CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_SINGLE_RAIL - {CmdRemoveSingleRail, CMD_AUTO}, // CMD_REMOVE_SINGLE_RAIL - {CmdLandscapeClear, 0}, // CMD_LANDSCAPE_CLEAR - {CmdBuildBridge, CMD_AUTO}, // CMD_BUILD_BRIDGE - {CmdBuildRailStation, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_RAIL_STATION - {CmdBuildTrainDepot, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_TRAIN_DEPOT - {CmdBuildSingleSignal, CMD_AUTO}, // CMD_BUILD_SIGNALS - {CmdRemoveSingleSignal, CMD_AUTO}, // CMD_REMOVE_SIGNALS - {CmdTerraformLand, CMD_ALL_TILES | CMD_AUTO}, // CMD_TERRAFORM_LAND - {CmdPurchaseLandArea, CMD_NO_WATER | CMD_AUTO}, // CMD_PURCHASE_LAND_AREA - {CmdSellLandArea, 0}, // CMD_SELL_LAND_AREA - {CmdBuildTunnel, CMD_AUTO}, // CMD_BUILD_TUNNEL - {CmdRemoveFromRailStation, 0}, // CMD_REMOVE_FROM_RAIL_STATION - {CmdConvertRail, 0}, // CMD_CONVERT_RAILD - {CmdBuildRailWaypoint, 0}, // CMD_BUILD_RAIL_WAYPOINT - {CmdRenameWaypoint, 0}, // CMD_RENAME_WAYPOINT - {CmdRemoveFromRailWaypoint, 0}, // CMD_REMOVE_FROM_RAIL_WAYPOINT + DEF_CMD(CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_RAILROAD_TRACK + DEF_CMD(CmdRemoveRailroadTrack, CMD_AUTO), // CMD_REMOVE_RAILROAD_TRACK + DEF_CMD(CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_SINGLE_RAIL + DEF_CMD(CmdRemoveSingleRail, CMD_AUTO), // CMD_REMOVE_SINGLE_RAIL + DEF_CMD(CmdLandscapeClear, 0), // CMD_LANDSCAPE_CLEAR + DEF_CMD(CmdBuildBridge, CMD_AUTO), // CMD_BUILD_BRIDGE + DEF_CMD(CmdBuildRailStation, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_RAIL_STATION + DEF_CMD(CmdBuildTrainDepot, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_TRAIN_DEPOT + DEF_CMD(CmdBuildSingleSignal, CMD_AUTO), // CMD_BUILD_SIGNALS + DEF_CMD(CmdRemoveSingleSignal, CMD_AUTO), // CMD_REMOVE_SIGNALS + DEF_CMD(CmdTerraformLand, CMD_ALL_TILES | CMD_AUTO), // CMD_TERRAFORM_LAND + DEF_CMD(CmdPurchaseLandArea, CMD_NO_WATER | CMD_AUTO), // CMD_PURCHASE_LAND_AREA + DEF_CMD(CmdSellLandArea, 0), // CMD_SELL_LAND_AREA + DEF_CMD(CmdBuildTunnel, CMD_AUTO), // CMD_BUILD_TUNNEL + DEF_CMD(CmdRemoveFromRailStation, 0), // CMD_REMOVE_FROM_RAIL_STATION + DEF_CMD(CmdConvertRail, 0), // CMD_CONVERT_RAILD + DEF_CMD(CmdBuildRailWaypoint, 0), // CMD_BUILD_RAIL_WAYPOINT + DEF_CMD(CmdRenameWaypoint, 0), // CMD_RENAME_WAYPOINT + DEF_CMD(CmdRemoveFromRailWaypoint, 0), // CMD_REMOVE_FROM_RAIL_WAYPOINT - {CmdBuildRoadStop, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_ROAD_STOP - {CmdRemoveRoadStop, 0}, // CMD_REMOVE_ROAD_STOP - {CmdBuildLongRoad, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_LONG_ROAD - {CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO}, // CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed. - {CmdBuildRoad, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_ROAD - {CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_ROAD_DEPOT + DEF_CMD(CmdBuildRoadStop, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_ROAD_STOP + DEF_CMD(CmdRemoveRoadStop, 0), // CMD_REMOVE_ROAD_STOP + DEF_CMD(CmdBuildLongRoad, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_LONG_ROAD + DEF_CMD(CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO), // CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed. + DEF_CMD(CmdBuildRoad, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_ROAD + DEF_CMD(CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_ROAD_DEPOT - {CmdBuildAirport, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_AIRPORT - {CmdBuildDock, CMD_AUTO}, // CMD_BUILD_DOCK - {CmdBuildShipDepot, CMD_AUTO}, // CMD_BUILD_SHIP_DEPOT - {CmdBuildBuoy, CMD_AUTO}, // CMD_BUILD_BUOY - {CmdPlantTree, CMD_AUTO}, // CMD_PLANT_TREE - {CmdBuildRailVehicle, 0}, // CMD_BUILD_RAIL_VEHICLE - {CmdMoveRailVehicle, 0}, // CMD_MOVE_RAIL_VEHICLE + DEF_CMD(CmdBuildAirport, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_AIRPORT + DEF_CMD(CmdBuildDock, CMD_AUTO), // CMD_BUILD_DOCK + DEF_CMD(CmdBuildShipDepot, CMD_AUTO), // CMD_BUILD_SHIP_DEPOT + DEF_CMD(CmdBuildBuoy, CMD_AUTO), // CMD_BUILD_BUOY + DEF_CMD(CmdPlantTree, CMD_AUTO), // CMD_PLANT_TREE + DEF_CMD(CmdBuildRailVehicle, 0), // CMD_BUILD_RAIL_VEHICLE + DEF_CMD(CmdMoveRailVehicle, 0), // CMD_MOVE_RAIL_VEHICLE - {CmdSellRailWagon, 0}, // CMD_SELL_RAIL_WAGON - {CmdSendTrainToDepot, 0}, // CMD_SEND_TRAIN_TO_DEPOT - {CmdForceTrainProceed, 0}, // CMD_FORCE_TRAIN_PROCEED - {CmdReverseTrainDirection, 0}, // CMD_REVERSE_TRAIN_DIRECTION + DEF_CMD(CmdSellRailWagon, 0), // CMD_SELL_RAIL_WAGON + DEF_CMD(CmdSendTrainToDepot, 0), // CMD_SEND_TRAIN_TO_DEPOT + DEF_CMD(CmdForceTrainProceed, 0), // CMD_FORCE_TRAIN_PROCEED + DEF_CMD(CmdReverseTrainDirection, 0), // CMD_REVERSE_TRAIN_DIRECTION - {CmdModifyOrder, 0}, // CMD_MODIFY_ORDER - {CmdSkipToOrder, 0}, // CMD_SKIP_TO_ORDER - {CmdDeleteOrder, 0}, // CMD_DELETE_ORDER - {CmdInsertOrder, 0}, // CMD_INSERT_ORDER + DEF_CMD(CmdModifyOrder, 0), // CMD_MODIFY_ORDER + DEF_CMD(CmdSkipToOrder, 0), // CMD_SKIP_TO_ORDER + DEF_CMD(CmdDeleteOrder, 0), // CMD_DELETE_ORDER + DEF_CMD(CmdInsertOrder, 0), // CMD_INSERT_ORDER - {CmdChangeServiceInt, 0}, // CMD_CHANGE_SERVICE_INT + DEF_CMD(CmdChangeServiceInt, 0), // CMD_CHANGE_SERVICE_INT - {CmdBuildIndustry, 0}, // CMD_BUILD_INDUSTRY - {CmdBuildCompanyHQ, CMD_NO_WATER | CMD_AUTO}, // CMD_BUILD_COMPANY_HQ - {CmdSetCompanyManagerFace, 0}, // CMD_SET_COMPANY_MANAGER_FACE - {CmdSetCompanyColour, 0}, // CMD_SET_COMPANY_COLOUR + DEF_CMD(CmdBuildIndustry, 0), // CMD_BUILD_INDUSTRY + DEF_CMD(CmdBuildCompanyHQ, CMD_NO_WATER | CMD_AUTO), // CMD_BUILD_COMPANY_HQ + DEF_CMD(CmdSetCompanyManagerFace, 0), // CMD_SET_COMPANY_MANAGER_FACE + DEF_CMD(CmdSetCompanyColour, 0), // CMD_SET_COMPANY_COLOUR - {CmdIncreaseLoan, 0}, // CMD_INCREASE_LOAN - {CmdDecreaseLoan, 0}, // CMD_DECREASE_LOAN + DEF_CMD(CmdIncreaseLoan, 0), // CMD_INCREASE_LOAN + DEF_CMD(CmdDecreaseLoan, 0), // CMD_DECREASE_LOAN - {CmdWantEnginePreview, 0}, // CMD_WANT_ENGINE_PREVIEW + DEF_CMD(CmdWantEnginePreview, 0), // CMD_WANT_ENGINE_PREVIEW - {CmdRenameVehicle, 0}, // CMD_RENAME_VEHICLE - {CmdRenameEngine, 0}, // CMD_RENAME_ENGINE + DEF_CMD(CmdRenameVehicle, 0), // CMD_RENAME_VEHICLE + DEF_CMD(CmdRenameEngine, 0), // CMD_RENAME_ENGINE - {CmdRenameCompany, 0}, // CMD_RENAME_COMPANY - {CmdRenamePresident, 0}, // CMD_RENAME_PRESIDENT + DEF_CMD(CmdRenameCompany, 0), // CMD_RENAME_COMPANY + DEF_CMD(CmdRenamePresident, 0), // CMD_RENAME_PRESIDENT - {CmdRenameStation, 0}, // CMD_RENAME_STATION + DEF_CMD(CmdRenameStation, 0), // CMD_RENAME_STATION - {CmdSellAircraft, 0}, // CMD_SELL_AIRCRAFT + DEF_CMD(CmdSellAircraft, 0), // CMD_SELL_AIRCRAFT - {CmdBuildAircraft, 0}, // CMD_BUILD_AIRCRAFT - {CmdSendAircraftToHangar, 0}, // CMD_SEND_AIRCRAFT_TO_HANGAR - {CmdRefitAircraft, 0}, // CMD_REFIT_AIRCRAFT + DEF_CMD(CmdBuildAircraft, 0), // CMD_BUILD_AIRCRAFT + DEF_CMD(CmdSendAircraftToHangar, 0), // CMD_SEND_AIRCRAFT_TO_HANGAR + DEF_CMD(CmdRefitAircraft, 0), // CMD_REFIT_AIRCRAFT - {CmdPlaceSign, 0}, // CMD_PLACE_SIGN - {CmdRenameSign, 0}, // CMD_RENAME_SIGN + DEF_CMD(CmdPlaceSign, 0), // CMD_PLACE_SIGN + DEF_CMD(CmdRenameSign, 0), // CMD_RENAME_SIGN - {CmdBuildRoadVeh, 0}, // CMD_BUILD_ROAD_VEH - {CmdSellRoadVeh, 0}, // CMD_SELL_ROAD_VEH - {CmdSendRoadVehToDepot, 0}, // CMD_SEND_ROADVEH_TO_DEPOT - {CmdTurnRoadVeh, 0}, // CMD_TURN_ROADVEH - {CmdRefitRoadVeh, 0}, // CMD_REFIT_ROAD_VEH + DEF_CMD(CmdBuildRoadVeh, 0), // CMD_BUILD_ROAD_VEH + DEF_CMD(CmdSellRoadVeh, 0), // CMD_SELL_ROAD_VEH + DEF_CMD(CmdSendRoadVehToDepot, 0), // CMD_SEND_ROADVEH_TO_DEPOT + DEF_CMD(CmdTurnRoadVeh, 0), // CMD_TURN_ROADVEH + DEF_CMD(CmdRefitRoadVeh, 0), // CMD_REFIT_ROAD_VEH - {CmdPause, CMD_SERVER}, // CMD_PAUSE + DEF_CMD(CmdPause, CMD_SERVER), // CMD_PAUSE - {CmdBuyShareInCompany, 0}, // CMD_BUY_SHARE_IN_COMPANY - {CmdSellShareInCompany, 0}, // CMD_SELL_SHARE_IN_COMPANY - {CmdBuyCompany, 0}, // CMD_BUY_COMANY + DEF_CMD(CmdBuyShareInCompany, 0), // CMD_BUY_SHARE_IN_COMPANY + DEF_CMD(CmdSellShareInCompany, 0), // CMD_SELL_SHARE_IN_COMPANY + DEF_CMD(CmdBuyCompany, 0), // CMD_BUY_COMANY - {CmdFoundTown, CMD_NO_TEST}, // CMD_FOUND_TOWN; founding random town can fail only in exec run - {CmdRenameTown, CMD_SERVER}, // CMD_RENAME_TOWN - {CmdDoTownAction, 0}, // CMD_DO_TOWN_ACTION + DEF_CMD(CmdFoundTown, CMD_NO_TEST), // CMD_FOUND_TOWN; founding random town can fail only in exec run + DEF_CMD(CmdRenameTown, CMD_SERVER), // CMD_RENAME_TOWN + DEF_CMD(CmdDoTownAction, 0), // CMD_DO_TOWN_ACTION - {CmdSellShip, 0}, // CMD_SELL_SHIP - {CmdBuildShip, 0}, // CMD_BUILD_SHIP - {CmdSendShipToDepot, 0}, // CMD_SEND_SHIP_TO_DEPOT - {CmdRefitShip, 0}, // CMD_REFIT_SHIP + DEF_CMD(CmdSellShip, 0), // CMD_SELL_SHIP + DEF_CMD(CmdBuildShip, 0), // CMD_BUILD_SHIP + DEF_CMD(CmdSendShipToDepot, 0), // CMD_SEND_SHIP_TO_DEPOT + DEF_CMD(CmdRefitShip, 0), // CMD_REFIT_SHIP - {CmdOrderRefit, 0}, // CMD_ORDER_REFIT - {CmdCloneOrder, 0}, // CMD_CLONE_ORDER + DEF_CMD(CmdOrderRefit, 0), // CMD_ORDER_REFIT + DEF_CMD(CmdCloneOrder, 0), // CMD_CLONE_ORDER - {CmdClearArea, CMD_NO_TEST}, // CMD_CLEAR_AREA; destroying multi-tile houses makes town rating differ between test and execution + DEF_CMD(CmdClearArea, CMD_NO_TEST), // CMD_CLEAR_AREA; destroying multi-tile houses makes town rating differ between test and execution - {CmdMoneyCheat, CMD_OFFLINE}, // CMD_MONEY_CHEAT - {CmdBuildCanal, CMD_AUTO}, // CMD_BUILD_CANAL - {CmdCompanyCtrl, CMD_SPECTATOR}, // CMD_COMPANY_CTRL + DEF_CMD(CmdMoneyCheat, CMD_OFFLINE), // CMD_MONEY_CHEAT + DEF_CMD(CmdBuildCanal, CMD_AUTO), // CMD_BUILD_CANAL + DEF_CMD(CmdCompanyCtrl, CMD_SPECTATOR), // CMD_COMPANY_CTRL - {CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO}, // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once + DEF_CMD(CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO), // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once - {CmdRefitRailVehicle, 0}, // CMD_REFIT_RAIL_VEHICLE - {CmdRestoreOrderIndex, 0}, // CMD_RESTORE_ORDER_INDEX - {CmdBuildLock, CMD_AUTO}, // CMD_BUILD_LOCK + DEF_CMD(CmdRefitRailVehicle, 0), // CMD_REFIT_RAIL_VEHICLE + DEF_CMD(CmdRestoreOrderIndex, 0), // CMD_RESTORE_ORDER_INDEX + DEF_CMD(CmdBuildLock, CMD_AUTO), // CMD_BUILD_LOCK - {CmdBuildSignalTrack, CMD_AUTO}, // CMD_BUILD_SIGNAL_TRACK - {CmdRemoveSignalTrack, CMD_AUTO}, // CMD_REMOVE_SIGNAL_TRACK + DEF_CMD(CmdBuildSignalTrack, CMD_AUTO), // CMD_BUILD_SIGNAL_TRACK + DEF_CMD(CmdRemoveSignalTrack, CMD_AUTO), // CMD_REMOVE_SIGNAL_TRACK - {CmdGiveMoney, 0}, // CMD_GIVE_MONEY - {CmdChangeSetting, CMD_SERVER}, // CMD_CHANGE_SETTING - {CmdChangeCompanySetting, 0}, // CMD_CHANGE_COMPANY_SETTING - {CmdSetAutoReplace, 0}, // CMD_SET_AUTOREPLACE - {CmdCloneVehicle, CMD_NO_TEST}, // CMD_CLONE_VEHICLE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost - {CmdStartStopVehicle, 0}, // CMD_START_STOP_VEHICLE - {CmdMassStartStopVehicle, 0}, // CMD_MASS_START_STOP - {CmdAutoreplaceVehicle, 0}, // CMD_AUTOREPLACE_VEHICLE - {CmdDepotSellAllVehicles, 0}, // CMD_DEPOT_SELL_ALL_VEHICLES - {CmdDepotMassAutoReplace, 0}, // CMD_DEPOT_MASS_AUTOREPLACE - {CmdCreateGroup, 0}, // CMD_CREATE_GROUP - {CmdDeleteGroup, 0}, // CMD_DELETE_GROUP - {CmdRenameGroup, 0}, // CMD_RENAME_GROUP - {CmdAddVehicleGroup, 0}, // CMD_ADD_VEHICLE_GROUP - {CmdAddSharedVehicleGroup, 0}, // CMD_ADD_SHARE_VEHICLE_GROUP - {CmdRemoveAllVehiclesGroup, 0}, // CMD_REMOVE_ALL_VEHICLES_GROUP - {CmdSetGroupReplaceProtection, 0}, // CMD_SET_GROUP_REPLACE_PROTECTION - {CmdMoveOrder, 0}, // CMD_MOVE_ORDER - {CmdChangeTimetable, 0}, // CMD_CHANGE_TIMETABLE - {CmdSetVehicleOnTime, 0}, // CMD_SET_VEHICLE_ON_TIME - {CmdAutofillTimetable, 0}, // CMD_AUTOFILL_TIMETABLE - {CmdSetTimetableStart, 0}, // CMD_SET_TIMETABLE_START + DEF_CMD(CmdGiveMoney, 0), // CMD_GIVE_MONEY + DEF_CMD(CmdChangeSetting, CMD_SERVER), // CMD_CHANGE_SETTING + DEF_CMD(CmdChangeCompanySetting, 0), // CMD_CHANGE_COMPANY_SETTING + DEF_CMD(CmdSetAutoReplace, 0), // CMD_SET_AUTOREPLACE + DEF_CMD(CmdCloneVehicle, CMD_NO_TEST), // CMD_CLONE_VEHICLE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost + DEF_CMD(CmdStartStopVehicle, 0), // CMD_START_STOP_VEHICLE + DEF_CMD(CmdMassStartStopVehicle, 0), // CMD_MASS_START_STOP + DEF_CMD(CmdAutoreplaceVehicle, 0), // CMD_AUTOREPLACE_VEHICLE + DEF_CMD(CmdDepotSellAllVehicles, 0), // CMD_DEPOT_SELL_ALL_VEHICLES + DEF_CMD(CmdDepotMassAutoReplace, 0), // CMD_DEPOT_MASS_AUTOREPLACE + DEF_CMD(CmdCreateGroup, 0), // CMD_CREATE_GROUP + DEF_CMD(CmdDeleteGroup, 0), // CMD_DELETE_GROUP + DEF_CMD(CmdRenameGroup, 0), // CMD_RENAME_GROUP + DEF_CMD(CmdAddVehicleGroup, 0), // CMD_ADD_VEHICLE_GROUP + DEF_CMD(CmdAddSharedVehicleGroup, 0), // CMD_ADD_SHARE_VEHICLE_GROUP + DEF_CMD(CmdRemoveAllVehiclesGroup, 0), // CMD_REMOVE_ALL_VEHICLES_GROUP + DEF_CMD(CmdSetGroupReplaceProtection, 0), // CMD_SET_GROUP_REPLACE_PROTECTION + DEF_CMD(CmdMoveOrder, 0), // CMD_MOVE_ORDER + DEF_CMD(CmdChangeTimetable, 0), // CMD_CHANGE_TIMETABLE + DEF_CMD(CmdSetVehicleOnTime, 0), // CMD_SET_VEHICLE_ON_TIME + DEF_CMD(CmdAutofillTimetable, 0), // CMD_AUTOFILL_TIMETABLE + DEF_CMD(CmdSetTimetableStart, 0), // CMD_SET_TIMETABLE_START }; /*! @@ -362,6 +364,20 @@ byte GetCommandFlags(uint32 cmd) return _command_proc_table[cmd & CMD_ID_MASK].flags; } +/*! + * This function mask the parameter with CMD_ID_MASK and returns + * the name which belongs to the given command. + * + * @param cmd The integer value of the command + * @return The name for this command + */ +const char *GetCommandName(uint32 cmd) +{ + assert(IsValidCommand(cmd)); + + return _command_proc_table[cmd & CMD_ID_MASK].name; +} + static int _docommand_recursive = 0; /** @@ -640,7 +656,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, return_dcpi(CommandCost(), false); } #endif /* ENABLE_NETWORK */ - DEBUG(desync, 1, "cmd: %08x; %08x; %1x; %06x; %08x; %08x; %04x; %s\n", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text); + DEBUG(desync, 1, "cmd: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text, GetCommandName(cmd)); /* Actually try and execute the command. If no cost-type is given * use the construction one */ diff --git a/src/command_func.h b/src/command_func.h index f5a25d67c8..67a05e6854 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -67,6 +67,10 @@ bool IsValidCommand(uint32 cmd); * Returns the flags from a given command. */ byte GetCommandFlags(uint32 cmd); +/** + * Returns the name of a given command. + */ +const char *GetCommandName(uint32 cmd); /** * Returns the current money available which can be used for a command. */ diff --git a/src/command_type.h b/src/command_type.h index fb71eac27c..3bc229273c 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -382,8 +382,9 @@ typedef CommandCost CommandProc(TileIndex tile, DoCommandFlag flags, uint32 p1, * the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values. */ struct Command { - CommandProc *proc; - byte flags; + CommandProc *proc; ///< The procedure to actually executing + const char *name; ///< A human readable name for the procedure + byte flags; ///< The (command) flags to that apply to this command }; /** diff --git a/src/date.cpp b/src/date.cpp index 9122b21c88..944e40bc41 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -218,12 +218,6 @@ static void OnNewYear() */ static void OnNewMonth() { - if (_debug_desync_level > 2) { - char name[MAX_PATH]; - snprintf(name, lengthof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); - SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR); - } - if (_settings_client.gui.autosave != 0 && (_cur_month % _autosave_months[_settings_client.gui.autosave]) == 0) { _do_autosave = true; RedrawAutosave(); diff --git a/src/debug.cpp b/src/debug.cpp index 391730f5ed..8ef91bed0a 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -93,7 +93,7 @@ static void debug_print(const char *dbg, const char *buf) static FILE *f = FioFOpenFile("commands-out.log", "wb", AUTOSAVE_DIR); if (f == NULL) return; - fprintf(f, "%s%s", GetLogPrefix(), buf); + fprintf(f, "%s%s\n", GetLogPrefix(), buf); fflush(f); } } diff --git a/src/genworld.cpp b/src/genworld.cpp index 973bf89ffd..3b0f2260d9 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -176,7 +176,7 @@ static void _GenerateWorld(void *) ShowNewGRFError(); if (_network_dedicated) DEBUG(net, 0, "Map generated, starting game"); - DEBUG(desync, 1, "new_map: %i\n", _settings_game.game_creation.generation_seed); + DEBUG(desync, 1, "new_map: %08x", _settings_game.game_creation.generation_seed); if (_settings_client.gui.pause_on_newgame && _game_mode == GM_NORMAL) DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE); if (_debug_desync_level > 0) { diff --git a/src/network/network.cpp b/src/network/network.cpp index 6d574cceba..87c7fe955a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -35,11 +35,14 @@ #include "../rev.h" #include "../core/pool_func.hpp" #include "../gfx_func.h" -#ifdef DEBUG_DUMP_COMMANDS - #include "../fileio_func.h" -#endif /* DEBUG_DUMP_COMMANDS */ #include "table/strings.h" +#ifdef DEBUG_DUMP_COMMANDS +#include "../fileio_func.h" +/** When running the server till the wait point, run as fast as we can! */ +bool _ddc_fastforward = true; +#endif /* DEBUG_DUMP_COMMANDS */ + DECLARE_POSTFIX_INCREMENT(ClientID); assert_compile(NetworkClientInfoPool::MAX_SIZE == NetworkClientSocketPool::MAX_SIZE); @@ -249,7 +252,7 @@ void NetworkTextMessage(NetworkAction action, ConsoleColour colour, bool self_se SetDParam(2, data); GetString(message, strid, lastof(message)); - DEBUG(desync, 1, "msg: %d; %d; %s\n", _date, _date_fract, message); + DEBUG(desync, 1, "msg: %08x; %02x; %s", _date, _date_fract, message); IConsolePrintF(colour, "%s", message); NetworkAddChatMessage((TextColour)colour, duration, "%s", message); } @@ -1024,7 +1027,7 @@ static bool NetworkDoClientLoop() if (_sync_seed_1 != _random.state[0]) { #endif NetworkError(STR_NETWORK_ERROR_DESYNC); - DEBUG(desync, 1, "sync_err: %d; %d\n", _date, _date_fract); + DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract); DEBUG(net, 0, "Sync error detected!"); NetworkClientError(NETWORK_RECV_STATUS_DESYNC, NetworkClientSocket::Get(0)); return false; @@ -1074,20 +1077,26 @@ void NetworkGameLoop() if (!NetworkReceive()) return; if (_network_server) { + /* Log the sync state to check for in-syncedness of replays. */ + if (_date_fract == 0) DEBUG(desync, 1, "sync: %08x; %02x; %08x; %08x", _date, _date_fract, _random.state[0], _random.state[1]); + #ifdef DEBUG_DUMP_COMMANDS + /* Loading of the debug commands from -ddesync>=1 */ 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"); + DEBUG(net, 0, "Cannot open commands.log"); next_date = 1; } while (f != NULL && !feof(f)) { if (cp != NULL && _date == next_date && _date_fract == next_date_fract) { _current_company = cp->company; - DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd, NULL, cp->text); + bool ret = DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd, NULL, cp->text); + DEBUG(net, 0, "injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s) -> %i", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd), (int)ret); + assert(ret); free(cp); cp = NULL; } @@ -1096,11 +1105,41 @@ void NetworkGameLoop() char buff[4096]; if (fgets(buff, lengthof(buff), f) == NULL) break; - if (strncmp(buff, "cmd: ", 8) != 0) continue; - cp = MallocT(1); - int company; - sscanf(&buff[8], "%d; %d; %d; %d; %d; %d; %d; %s", &next_date, &next_date_fract, &company, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text); - cp->company = (CompanyID)company; + + char *p = buff; + /* Ignore the "[date time] " part of the message */ + if (*p == '[') { + p = strchr(p, ']'); + if (p == NULL) break; + p += 2; + } + + if (strncmp(p, "cmd: ", 5) == 0) { + cp = CallocT(1); + int company; + int ret = sscanf(p + 5, "%x; %x; %x; %x; %x; %x; %x; \"%[^\"]\"", &next_date, &next_date_fract, &company, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text); + /* There are 8 pieces of data to read, however the last is a + * string that might or might not exist. Ignore it if that + * string misses because in 99% of the time it's not used. */ + assert(ret == 8 || ret == 7); + cp->company = (CompanyID)company; + } else if (strncmp(p, "join: ", 6) == 0) { + /* Manually insert a pause when joining; this way the client can join at the exact right time. */ + int ret = sscanf(p + 6, "%x; %x", &next_date, &next_date_fract); + assert(ret == 2); + DEBUG(net, 0, "injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract); + cp = MallocT(1); + cp->company = COMPANY_SPECTATOR; + cp->cmd = CMD_PAUSE; + cp->p1 = PM_PAUSED_NORMAL; + cp->p2 = 1; + _ddc_fastforward = false; + } + } + if (f != NULL && feof(f)) { + DEBUG(net, 0, "End of commands.log"); + fclose(f); + f = NULL; } #endif /* DEBUG_DUMP_COMMANDS */ if (_frame_counter >= _frame_counter_max) { diff --git a/src/network/network_internal.h b/src/network/network_internal.h index bb681bc061..ef1030a9ea 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -38,6 +38,18 @@ #define NETWORK_SEND_DOUBLE_SEED #endif /* RANDOM_DEBUG */ +/** + * Helper variable to make the dedicated server go fast until the (first) join. + * Used to load the desync debug logs, i.e. for reproducing a desync. + * There's basically no need to ever enable this, unless you really know what + * you are doing, i.e. debugging a desync. + */ +#ifdef DEBUG_DUMP_COMMANDS +extern bool _ddc_fastforward; +#else +#define _ddc_fastforward (false) +#endif /* DEBUG_DUMP_COMMANDS */ + enum MapPacket { MAP_PACKET_START, MAP_PACKET_NORMAL, diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index c090d176d2..f5fd7fcde3 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -716,6 +716,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_JOIN) strecpy(ci->client_name, name, lastof(ci->client_name)); ci->client_playas = playas; ci->client_lang = client_lang; + DEBUG(desync, 1, "client: %08x; %02x; %02x; %04x", _date, _date_fract, (int)ci->client_playas, ci->index); /* Make sure companies to which people try to join are not autocleaned */ if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0; @@ -1396,6 +1397,8 @@ void NetworkUpdateClientInfo(ClientID client_id) if (ci == NULL) return; + DEBUG(desync, 1, "client: %08x; %02x; %02x; %04x", _date, _date_fract, (int)ci->client_playas, client_id); + FOR_ALL_CLIENT_SOCKETS(cs) { SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, ci); } diff --git a/src/openttd.cpp b/src/openttd.cpp index f5a4f8ac8b..3e5a3dbf63 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1110,16 +1110,18 @@ static void CheckCaches() Vehicle *v; FOR_ALL_VEHICLES(v) { - if (v != v->First()) continue; + if (v != v->First() || v->vehstatus & VS_CRASHED) continue; switch (v->type) { case VEH_ROAD: { RoadVehicle *rv = RoadVehicle::From(v); - RoadVehicleCache cache = rv->rcache; + RoadVehicleCache cache; + memset(&cache, 0, sizeof(cache)); + cache = rv->rcache; RoadVehUpdateCache(rv); if (memcmp(&cache, &rv->rcache, sizeof(RoadVehicleCache)) != 0) { - DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i\n", v->index, (int)v->owner, v->unitnumber); + DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i", v->index, (int)v->owner, v->unitnumber); } } break; @@ -1128,7 +1130,7 @@ static void CheckCaches() Train *t = Train::From(v); for (Vehicle *u = t; u != NULL; u = u->Next()) length++; - TrainCache *wagons = MallocT(length); + TrainCache *wagons = CallocT(length); length = 0; for (Train *u = t; u != NULL; u = u->Next()) wagons[length++] = u->tcache; @@ -1137,7 +1139,7 @@ static void CheckCaches() length = 0; for (Train *u = t; u != NULL; u = u->Next()) { if (memcmp(&wagons[length], &u->tcache, sizeof(TrainCache)) != 0) { - DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i, wagon %i\n", v->index, (int)v->owner, v->unitnumber, length); + DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i, wagon %i", v->index, (int)v->owner, v->unitnumber, length); } length++; } @@ -1147,11 +1149,13 @@ static void CheckCaches() case VEH_AIRCRAFT: { Aircraft *a = Aircraft::From(v); - AircraftCache cache = a->acache; + AircraftCache cache; + memset(&cache, 0, sizeof(cache)); + cache = a->acache; UpdateAircraftCache(a); if (memcmp(&cache, &a->acache, sizeof(AircraftCache)) != 0) { - DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i\n", v->index, (int)v->owner, v->unitnumber); + DEBUG(desync, 2, "cache mismatch: vehicle %i, company %i, unit number %i", v->index, (int)v->owner, v->unitnumber); } } break; @@ -1206,6 +1210,13 @@ void StateGameLoop() } else { CheckCaches(); + if (_debug_desync_level > 2 && _date_fract == 0 && (_date & 0x1F) == 0) { + /* Save the desync savegame if needed. */ + char name[MAX_PATH]; + snprintf(name, lengthof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); + SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR); + } + /* All these actions has to be done from OWNER_NONE * for multiplayer compatibility */ CompanyID old_company = _current_company; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 0d081df730..4e0228657b 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1882,7 +1882,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo /* General tactic is to first save the game to memory, then use an available writer * to write it to file, either in threaded mode if possible, or single-threaded */ if (mode == SL_SAVE) { // SAVE game - DEBUG(desync, 1, "save: %s\n", filename); + DEBUG(desync, 1, "save: %s", filename); _sl.write_bytes = WriteMem; _sl.excpt_uninit = UnInitMem; @@ -1906,7 +1906,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo } } else { // LOAD game assert(mode == SL_LOAD); - DEBUG(desync, 1, "load: %s\n", filename); + DEBUG(desync, 1, "load: %s", filename); /* Can't fseek to 0 as in tar files that is not correct */ long pos = ftell(_sl.fh); diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index 69de090ab8..37797f7e4d 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -300,13 +300,15 @@ void VideoDriver_Dedicated::MainLoop() cur_ticks = GetTime(); _realtime_tick += cur_ticks - prev_cur_ticks; - if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) { + if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks || _ddc_fastforward) { next_tick = cur_ticks + 30; GameLoop(); UpdateWindows(); } - CSleep(1); + + /* Don't sleep when fast forwarding (for desync debugging) */ + if (!_ddc_fastforward) CSleep(1); } }