diff --git a/src/command.cpp b/src/command.cpp index 84070993bc..6dd49ef6dc 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -29,6 +29,7 @@ #include "newgrf_text.h" #include "string_func.h" #include "scope_info.h" +#include #include "table/strings.h" @@ -441,6 +442,87 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdDesyncCheck, CMD_SERVER, CMDT_SERVER_SETTING ), // CMD_DESYNC_CHECK }; + +/** + * List of flags for a command log entry + */ +enum CommandLogEntryFlagEnum { + CLEF_NONE = 0x00, ///< no flag is set + CLEF_CMD_FAILED = 0x01, ///< command failed + CLEF_GENERATING_WORLD = 0x02, ///< generating world + CLEF_TEXT = 0x04, ///< have command text + CLEF_ESTIMATE_ONLY = 0x08, ///< estimate only + CLEF_ONLY_SENDING = 0x10, ///< only sending + CLEF_MY_CMD = 0x20, ///< locally generated command + CLEF_BINARY = 0x40, ///< binary_length is > 0 +}; +DECLARE_ENUM_AS_BIT_SET(CommandLogEntryFlagEnum) +typedef SimpleTinyEnumT CommandLogEntryFlag; + +struct CommandLogEntry { + TileIndex tile; + uint32 p1; + uint32 p2; + uint32 cmd; + Date date; + DateFract date_fract; + uint8 tick_skip_counter; + CompanyByte current_company; + CompanyByte local_company; + CommandLogEntryFlag log_flags; + + CommandLogEntry() { } + + CommandLogEntry(const CommandCost &res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandLogEntryFlag log_flags) + : tile(tile), p1(p1), p2(p2), cmd(cmd), date(_date), date_fract(_date_fract), tick_skip_counter(_tick_skip_counter), + current_company(_current_company), local_company(_local_company), log_flags(log_flags) + { + if (res.Failed()) this->log_flags |= CLEF_CMD_FAILED; + if (_generating_world) this->log_flags |= CLEF_GENERATING_WORLD; + } +}; + +static std::array command_log; +static unsigned int command_log_count = 0; +static unsigned int command_log_next = 0; + +void ClearCommandLog() +{ + command_log_count = 0; + command_log_next = 0; +} + +char *DumpCommandLog(char *buffer, const char *last) +{ + const unsigned int count = min(command_log_count, command_log.size()); + unsigned int log_index = command_log_next; + + buffer += seprintf(buffer, last, "Command Log:\n Showing most recent %u of %u commands\n", count, command_log_count); + + for (unsigned int i = 0 ; i < count; i++) { + if (log_index > 0) { + log_index--; + } else { + log_index = command_log.size() - 1; + } + const CommandLogEntry &entry = command_log[log_index]; + + auto fc = [&](CommandLogEntryFlagEnum flag, char c) -> char { + return entry.log_flags & flag ? c : '-'; + }; + + YearMonthDay ymd; + ConvertDateToYMD(entry.date, &ymd); + buffer += seprintf(buffer, last, " %3u | %4i-%02i-%02i, %2i, %3i | ", i, ymd.year, ymd.month + 1, ymd.day, entry.date_fract, entry.tick_skip_counter); + buffer += seprintf(buffer, last, "%c%c%c%c%c%c%c | ", + fc(CLEF_BINARY, 'b'), fc(CLEF_MY_CMD, 'm'), fc(CLEF_ONLY_SENDING, 's'), fc(CLEF_ESTIMATE_ONLY, 'e'), + fc(CLEF_TEXT, 't'), fc(CLEF_GENERATING_WORLD, 'g'), fc(CLEF_CMD_FAILED, 'f')); + buffer += seprintf(buffer, last, " %7d x %7d, p1: 0x%08X, p2: 0x%08X, cc: %2u, lc: %2u, cmd: 0x%08X (%s)\n", + TileX(entry.tile), TileY(entry.tile), entry.p1, entry.p2, (uint) entry.current_company, (uint) entry.local_company, entry.cmd, GetCommandName(entry.cmd)); + } + return buffer; +} + /*! * This function range-checks a cmd, and checks if the cmd is not NULL * @@ -688,6 +770,17 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac callback(res, tile, p1, p2); } + CommandLogEntryFlag log_flags; + log_flags = CLEF_NONE; + if (!StrEmpty(text)) log_flags |= CLEF_TEXT; + if (estimate_only) log_flags |= CLEF_ESTIMATE_ONLY; + if (only_sending) log_flags |= CLEF_ONLY_SENDING; + if (my_cmd) log_flags |= CLEF_MY_CMD; + if (binary_length > 0) log_flags |= CLEF_BINARY; + command_log[command_log_next] = CommandLogEntry(res, tile, p1, p2, cmd, log_flags); + command_log_next = (command_log_next + 1) % command_log.size(); + command_log_count++; + return res.Succeeded(); } diff --git a/src/command_func.h b/src/command_func.h index 4b0830b5f0..5c585f50b0 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -68,6 +68,9 @@ static inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags) return flags; } +void ClearCommandLog(); +char *DumpCommandLog(char *buffer, const char *last); + /*** All command callbacks that exist ***/ /* ai/ai_instance.cpp */ diff --git a/src/console.cpp b/src/console.cpp index 511019281a..24a528d8a4 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -253,13 +253,14 @@ char *RemoveUnderscores(char *name) * @param name name of the command that will be used * @param proc function that will be called upon execution of command */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook) +void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) { IConsoleCmd *item_new = MallocT(1); item_new->name = RemoveUnderscores(stredup(name)); item_new->next = NULL; item_new->proc = proc; item_new->hook = hook; + item_new->unlisted = unlisted; IConsoleAddSorted(&_iconsole_cmds, item_new); } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 31217d4f42..bf7409ced0 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1525,7 +1525,7 @@ DEF_CONSOLE_CMD(ConListCommands) for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != NULL; cmd = cmd->next) { if (argv[1] == NULL || strstr(cmd->name, argv[1]) != NULL) { - if (cmd->hook == NULL || cmd->hook(false) != CHR_HIDE) IConsolePrintF(CC_DEFAULT, "%s", cmd->name); + if (cmd->unlisted == false && (cmd->hook == NULL || cmd->hook(false) != CHR_HIDE)) IConsolePrintF(CC_DEFAULT, "%s", cmd->name); } } @@ -1939,7 +1939,18 @@ DEF_CONSOLE_CMD(ConResetBlockedHeliports) return true; } +DEF_CONSOLE_CMD(ConDumpCommandLog) +{ + if (argc == 0) { + IConsoleHelp("Dump log of recently executed commands."); + return true; + } + char buffer[32768]; + DumpCommandLog(buffer, lastof(buffer)); + PrintLineByLine(buffer); + return true; +} #ifdef _DEBUG /****************** @@ -2085,6 +2096,7 @@ void IConsoleStdLibRegister() #ifdef _DEBUG IConsoleDebugLibRegister(); #endif + IConsoleCmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true); /* NewGRF development stuff */ IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); diff --git a/src/console_internal.h b/src/console_internal.h index 1b63b26f0d..5673f1ea17 100644 --- a/src/console_internal.h +++ b/src/console_internal.h @@ -40,6 +40,7 @@ struct IConsoleCmd { IConsoleCmdProc *proc; ///< process executed when command is typed IConsoleHook *hook; ///< any special trigger action that needs executing + bool unlisted; }; /** @@ -69,7 +70,7 @@ extern IConsoleAlias *_iconsole_aliases; ///< List of registered aliases. void IConsoleClearBuffer(); /* Commands */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook = NULL); +void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook = NULL, bool unlisted = false); void IConsoleAliasRegister(const char *name, const char *cmd); IConsoleCmd *IConsoleCmdGet(const char *name); IConsoleAlias *IConsoleAliasGet(const char *name); diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 9333aa2dc9..67f4f32a2d 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -29,6 +29,7 @@ #include "language.h" #include "fontcache.h" #include "scope_info.h" +#include "command_func.h" #include "thread/thread.h" #include "ai/ai_info.hpp" @@ -328,6 +329,19 @@ char *CrashLog::LogGamelog(char *buffer, const char *last) const return CrashLog::gamelog_buffer + seprintf(CrashLog::gamelog_buffer, last, "\n"); } +/** + * Writes the command log data to the buffer. + * @param buffer The begin where to write at. + * @param last The last position in the buffer to write to. + * @return the position of the \c '\0' character after the buffer. + */ +char *CrashLog::LogCommandLog(char *buffer, const char *last) const +{ + buffer = DumpCommandLog(buffer, last); + buffer += seprintf(buffer, last, "\n"); + return buffer; +} + /** * Fill the crash log buffer with all data of a crash log. * @param buffer The begin where to write at. @@ -362,6 +376,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const buffer = this->LogLibraries(buffer, last); buffer = this->LogModules(buffer, last); buffer = this->LogGamelog(buffer, last); + buffer = this->LogCommandLog(buffer, last); buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n"); return buffer; @@ -454,7 +469,7 @@ bool CrashLog::MakeCrashLog() const crashlogged = true; char filename[MAX_PATH]; - char buffer[65536]; + char buffer[65536 * 2]; bool ret = true; printf("Crash encountered, generating crash log...\n"); diff --git a/src/crashlog.h b/src/crashlog.h index 37ac9ae10e..c59f73aba8 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -103,6 +103,7 @@ protected: char *LogConfiguration(char *buffer, const char *last) const; char *LogLibraries(char *buffer, const char *last) const; char *LogGamelog(char *buffer, const char *last) const; + char *LogCommandLog(char *buffer, const char *last) const; public: /** Stub destructor to silence some compilers. */ diff --git a/src/misc.cpp b/src/misc.cpp index d24be86eba..5d4189f1bd 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -32,6 +32,7 @@ #include "programmable_signals.h" #include "viewport_func.h" #include "bridge_signal_map.h" +#include "command_func.h" #include "safeguards.h" @@ -64,6 +65,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin AllocateMap(size_x, size_y); ViewportMapClearTunnelCache(); + ClearCommandLog(); _pause_mode = PM_UNPAUSED; _fast_forward = 0; diff --git a/src/openttd.cpp b/src/openttd.cpp index 785dfb44eb..05ee7b4476 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -357,6 +357,7 @@ static void ShutdownGame() UninitFreeType(); ViewportMapClearTunnelCache(); + ClearCommandLog(); } /**