Forgotten sorce files for command log replay

This commit is contained in:
dP
2023-03-17 11:58:26 +04:00
parent f1d3835242
commit bd5a4db481
2 changed files with 225 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
#include "../stdafx.h"
#include "cm_command_log.hpp"
#include "cm_bitstream.hpp"
#include "../command_type.h"
#include "../network/network_internal.h"
#include "../network/network_server.h"
#include "../console_func.h"
#include "../rev.h"
#include "../strings_func.h"
#include <lzma.h>
#include <fstream>
#include <queue>
namespace citymania {
struct FakeCommand {
Date date;
DateFract date_fract;
uint res;
uint8 seed;
uint16 client_id;
CommandPacket cp;
};
static std::queue<FakeCommand> _fake_commands;
bool _replay_started = false;
bool DatePredate(Date date1, DateFract date_fract1, Date date2, DateFract date_fract2) {
return date1 < date2 || (date1 == date2 && date_fract1 < date_fract2);
}
void SkipFakeCommands(Date date, DateFract date_fract) {
uint commands_skipped = 0;
while (!_fake_commands.empty() && DatePredate(_fake_commands.front().date, _fake_commands.front().date_fract, date, date_fract)) {
_fake_commands.pop();
commands_skipped++;
}
if (commands_skipped) {
fprintf(stderr, "Skipped %u commands that predate the current date (%d/%hu)\n", commands_skipped, date, date_fract);
}
}
extern CommandCost ExecuteCommand(CommandPacket *cp);
void ExecuteFakeCommands(Date date, DateFract date_fract) {
if (!_replay_started) {
SkipFakeCommands(_date, _date_fract);
_replay_started = true;
}
auto backup_company = _current_company;
while (!_fake_commands.empty() && !DatePredate(date, date_fract, _fake_commands.front().date, _fake_commands.front().date_fract)) {
auto &x = _fake_commands.front();
fprintf(stderr, "Executing command: %s(%u) company=%u tile=%u ... ", GetCommandName(x.cp.cmd), x.cp.cmd, x.cp.company, (uint)x.cp.tile);
if (x.res == 0) {
fprintf(stderr, "REJECTED\n");
_fake_commands.pop();
continue;
}
if (_networking) {
x.cp.frame = _frame_counter_max + 1;
x.cp.callback = nullptr;
x.cp.my_cmd = false;
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
if (cs->status >= NetworkClientSocket::STATUS_MAP) {
cs->outgoing_queue.Append(&x.cp);
}
}
}
_current_company = (CompanyID)x.cp.company;
auto res = ExecuteCommand(&x.cp);
if (res.Failed() != (x.res != 1)) {
if (!res.Failed()) {
fprintf(stderr, "FAIL (Failing command succeeded)\n");
} else if (res.GetErrorMessage() != INVALID_STRING_ID) {
char buf[DRAW_STRING_BUFFER];
GetString(buf, res.GetErrorMessage(), lastof(buf));
fprintf(stderr, "FAIL (Successful command failed: %s)\n", buf);
} else {
fprintf(stderr, "FAIL (Successful command failed)\n");
}
} else {
fprintf(stderr, "OK\n");
}
if (x.seed != (_random.state[0] & 255)) {
fprintf(stderr, "*** DESYNC expected seed %u vs current %u ***\n", x.seed, _random.state[0] & 255);
}
_fake_commands.pop();
}
_current_company = backup_company;
}
void load_replay_commands(const std::string &filename, std::function<void(const std::string &)> error_func) {
_fake_commands = {};
FILE *f = std::fopen(filename.c_str(), "rb");
if (f == nullptr) {
error_func(fmt::format("Cannot open file `{}`: {}", filename, std::strerror(errno)));
return;
}
lzma_stream lzma = LZMA_STREAM_INIT;
lzma_ret ret = lzma_auto_decoder(&lzma, 1 << 28, 0);
if (ret != LZMA_OK) {
error_func(fmt::format("Cannot initialize LZMA decompressor (code {})", ret));
return;
}
static const size_t CHUNK_SIZE = 128 * 1024;
static byte inbuf[CHUNK_SIZE];
lzma.next_in = NULL;
lzma.avail_in = 0;
lzma_action action = LZMA_RUN;
u8vector data(CHUNK_SIZE);
size_t bytes_read = 0;
lzma.next_out = &data[0];
lzma.avail_out = data.size();
do {
fprintf(stderr, "LZMA in %d\n", (int)lzma.avail_in);
if (lzma.avail_in == 0 && !std::feof(f)) {
lzma.next_in = inbuf;
lzma.avail_in = std::fread((char *)inbuf, 1, sizeof(inbuf), f);
if (std::ferror(f)) {
error_func(fmt::format("Error reading {}: {}", filename, std::strerror(errno)));
return;
}
if (std::feof(f)) action = LZMA_FINISH;
}
ret = lzma_code(&lzma, action);
if (lzma.avail_out == 0 || ret == LZMA_STREAM_END) {
bytes_read += CHUNK_SIZE - lzma.avail_out;
data.resize(bytes_read + CHUNK_SIZE);
lzma.next_out = &data[bytes_read];
lzma.avail_out = CHUNK_SIZE;
}
} while (ret == LZMA_OK);
std::fclose(f);
data.resize(bytes_read);
if (ret != LZMA_STREAM_END) {
error_func(fmt::format("LZMA decompressor returned error code {}", ret));
// return;
}
auto bs = BitIStream(data);
auto version = bs.ReadBytes(2);
if (version != 1) {
error_func(fmt::format("Unsupported log file version {}", version));
return;
}
// auto openttd_version = bs.ReadBytes(4);
auto openttd_version = _openttd_newgrf_version;
if (_openttd_newgrf_version != openttd_version) {
error_func(fmt::format("OpenTTD version doesn't match: current {}, log file {}",
_openttd_newgrf_version, openttd_version));
return;
}
try {
while (!bs.eof()) {
FakeCommand fk;
auto date_full = bs.ReadBytes(4);
fk.date = date_full / DAY_TICKS;
fk.date_fract = date_full % DAY_TICKS;
fk.res = bs.ReadBytes(1);
fk.seed = bs.ReadBytes(1);
fk.cp.company = (Owner)bs.ReadBytes(1);
fk.client_id = bs.ReadBytes(2);
fk.cp.cmd = (Commands)bs.ReadBytes(2);
fk.cp.tile = bs.ReadBytes(4);
fk.cp.data = bs.ReadData();
fk.cp.callback = nullptr;
_fake_commands.push(fk);
error_func(fmt::format("Command {}({}) company={} client={} tile={}", GetCommandName(fk.cp.cmd), fk.cp.cmd, fk.cp.company, fk.client_id, fk.cp.tile));
}
}
catch (BitIStreamUnexpectedEnd &) {
error_func("Unexpected end of command data");
}
_replay_started = false;
}
bool IsReplayingCommands() {
return !_fake_commands.empty();
}
}; // namespace citymania

View File

@@ -0,0 +1,12 @@
#ifndef CM_COMMAND_LOG_HPP
#define CM_COMMAND_LOG_HPP
#include <functional>
namespace citymania {
void load_replay_commands(const std::string &filename, std::function<void(const std::string &)>);
}; // namespace citymania
#endif