don't skip server data commands before battle start
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Episode3 {
|
|||||||
void BattleRecord::PlayerEntry::print(FILE* stream) const {
|
void BattleRecord::PlayerEntry::print(FILE* stream) const {
|
||||||
// TODO: Format this nicely somehow. Maybe factor out the functions in
|
// TODO: Format this nicely somehow. Maybe factor out the functions in
|
||||||
// QuestScript that format some of these structures
|
// QuestScript that format some of these structures
|
||||||
print_data(stream, this, sizeof(this));
|
print_data(stream, this, sizeof(*this), 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleRecord::Event::Event(StringReader& r) {
|
BattleRecord::Event::Event(StringReader& r) {
|
||||||
@@ -96,29 +96,30 @@ void BattleRecord::Event::print(FILE* stream) const {
|
|||||||
for (const auto& player : this->players) {
|
for (const auto& player : this->players) {
|
||||||
fprintf(stream, " %02" PRIX32, player.lobby_data.client_id.load());
|
fprintf(stream, " %02" PRIX32, player.lobby_data.client_id.load());
|
||||||
}
|
}
|
||||||
|
fputc('\n', stream);
|
||||||
for (const auto& player : this->players) {
|
for (const auto& player : this->players) {
|
||||||
player.print(stream);
|
player.print(stream);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::BATTLE_COMMAND:
|
case Type::BATTLE_COMMAND:
|
||||||
fprintf(stream, "BATTLE_COMMAND\n");
|
fprintf(stream, "BATTLE_COMMAND\n");
|
||||||
print_data(stream, this->data);
|
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
break;
|
break;
|
||||||
case Type::GAME_COMMAND:
|
case Type::GAME_COMMAND:
|
||||||
fprintf(stream, "GAME_COMMAND\n");
|
fprintf(stream, "GAME_COMMAND\n");
|
||||||
print_data(stream, this->data);
|
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
break;
|
break;
|
||||||
case Type::EP3_GAME_COMMAND:
|
case Type::EP3_GAME_COMMAND:
|
||||||
fprintf(stream, "EP3_GAME_COMMAND\n");
|
fprintf(stream, "EP3_GAME_COMMAND\n");
|
||||||
print_data(stream, this->data);
|
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
break;
|
break;
|
||||||
case Type::CHAT_MESSAGE:
|
case Type::CHAT_MESSAGE:
|
||||||
fprintf(stream, "CHAT_MESSAGE %08" PRIX32 "\n", this->guild_card_number);
|
fprintf(stream, "CHAT_MESSAGE %08" PRIX32 "\n", this->guild_card_number);
|
||||||
print_data(stream, this->data);
|
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
break;
|
break;
|
||||||
case Type::SERVER_DATA_COMMAND:
|
case Type::SERVER_DATA_COMMAND:
|
||||||
fprintf(stream, "SERVER_DATA_COMMAND\n");
|
fprintf(stream, "SERVER_DATA_COMMAND\n");
|
||||||
print_data(stream, this->data);
|
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown event type in battle record");
|
throw runtime_error("unknown event type in battle record");
|
||||||
@@ -255,6 +256,20 @@ void BattleRecord::add_random_data(const void* data, size_t size) {
|
|||||||
this->random_stream.append(reinterpret_cast<const char*>(data), size);
|
this->random_stream.append(reinterpret_cast<const char*>(data), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<string> BattleRecord::get_all_server_data_commands() const {
|
||||||
|
vector<string> ret;
|
||||||
|
for (const auto& event : this->events) {
|
||||||
|
if (event.type == Event::Type::SERVER_DATA_COMMAND) {
|
||||||
|
ret.emplace_back(event.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string& BattleRecord::get_random_stream() const {
|
||||||
|
return this->random_stream;
|
||||||
|
}
|
||||||
|
|
||||||
bool BattleRecord::is_map_definition_event(const Event& ev) {
|
bool BattleRecord::is_map_definition_event(const Event& ev) {
|
||||||
if (ev.type == Event::Type::BATTLE_COMMAND) {
|
if (ev.type == Event::Type::BATTLE_COMMAND) {
|
||||||
auto& header = check_size_t<G_CardBattleCommandHeader>(ev.data, 0xFFFF);
|
auto& header = check_size_t<G_CardBattleCommandHeader>(ev.data, 0xFFFF);
|
||||||
@@ -331,7 +346,7 @@ void BattleRecord::set_battle_start_timestamp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; it != this->events.end(); it++) {
|
for (; it != this->events.end(); it++) {
|
||||||
if (it->type == Event::Type::BATTLE_COMMAND) {
|
if ((it->type == Event::Type::BATTLE_COMMAND) || (it->type == Event::Type::SERVER_DATA_COMMAND)) {
|
||||||
new_events.emplace_back(std::move(*it));
|
new_events.emplace_back(std::move(*it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,7 +388,7 @@ shared_ptr<const BattleRecord> BattleRecordPlayer::get_record() const {
|
|||||||
return this->record;
|
return this->record;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleRecordPlayer::set_lobby(std::shared_ptr<Lobby> l) {
|
void BattleRecordPlayer::set_lobby(shared_ptr<Lobby> l) {
|
||||||
this->lobby = l;
|
this->lobby = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ public:
|
|||||||
|
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
|
|
||||||
|
std::vector<std::string> get_all_server_data_commands() const;
|
||||||
|
const std::string& get_random_stream() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint64_t SIGNATURE_V1 = 0x14C946D56D1DAC50;
|
static constexpr uint64_t SIGNATURE_V1 = 0x14C946D56D1DAC50;
|
||||||
static constexpr uint64_t SIGNATURE_V2 = 0xD01E5EC12853C377;
|
static constexpr uint64_t SIGNATURE_V2 = 0xD01E5EC12853C377;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ void Server::PresenceEntry::clear() {
|
|||||||
|
|
||||||
Server::Server(shared_ptr<Lobby> lobby, Options&& options)
|
Server::Server(shared_ptr<Lobby> lobby, Options&& options)
|
||||||
: lobby(lobby),
|
: lobby(lobby),
|
||||||
battle_record(lobby->battle_record),
|
battle_record(lobby ? lobby->battle_record : nullptr),
|
||||||
has_lobby(lobby != nullptr),
|
has_lobby(lobby != nullptr),
|
||||||
options(std::move(options)),
|
options(std::move(options)),
|
||||||
last_chosen_map(this->options.tournament ? this->options.tournament->get_map() : nullptr),
|
last_chosen_map(this->options.tournament ? this->options.tournament->get_map() : nullptr),
|
||||||
@@ -260,7 +260,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
|
|||||||
|
|
||||||
} else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) &&
|
} else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) &&
|
||||||
this->log().info("Generated command")) {
|
this->log().info("Generated command")) {
|
||||||
print_data(stderr, data, size);
|
print_data(stderr, data, size, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,7 +1075,13 @@ shared_ptr<const PlayerState> Server::get_player_state(uint8_t client_id) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Server::get_random_raw() {
|
uint32_t Server::get_random_raw() {
|
||||||
le_uint32_t ret = random_from_optional_crypt(this->options.opt_rand_crypt);
|
le_uint32_t ret;
|
||||||
|
if (this->options.opt_rand_stream) {
|
||||||
|
this->options.opt_rand_stream->readx(&ret, sizeof(ret));
|
||||||
|
} else {
|
||||||
|
ret = random_from_optional_crypt(this->options.opt_rand_crypt);
|
||||||
|
}
|
||||||
|
|
||||||
if (this->battle_record && this->battle_record->writable()) {
|
if (this->battle_record && this->battle_record->writable()) {
|
||||||
this->battle_record->add_random_data(&ret, sizeof(ret));
|
this->battle_record->add_random_data(&ret, sizeof(ret));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public:
|
|||||||
std::shared_ptr<const CardIndex> card_index;
|
std::shared_ptr<const CardIndex> card_index;
|
||||||
std::shared_ptr<const MapIndex> map_index;
|
std::shared_ptr<const MapIndex> map_index;
|
||||||
uint32_t behavior_flags;
|
uint32_t behavior_flags;
|
||||||
|
std::shared_ptr<StringReader> opt_rand_stream;
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
||||||
std::shared_ptr<const Tournament> tournament;
|
std::shared_ptr<const Tournament> tournament;
|
||||||
std::array<std::vector<uint16_t>, 5> trap_card_ids;
|
std::array<std::vector<uint16_t>, 5> trap_card_ids;
|
||||||
|
|||||||
+43
-4
@@ -765,7 +765,7 @@ Action a_decrypt_dcv2_executable(
|
|||||||
If --simple is given, uses the simpler encryption method used in some\n\
|
If --simple is given, uses the simpler encryption method used in some\n\
|
||||||
community modifications of the game. In this case, --seed is not required;\n\
|
community modifications of the game. In this case, --seed is not required;\n\
|
||||||
if not given, finds the seed automatically, and prints it to stderr so you\n\
|
if not given, finds the seed automatically, and prints it to stderr so you\n\
|
||||||
will be able to use it when re-encrypting.",
|
will be able to use it when re-encrypting.\n",
|
||||||
+[](Arguments& args) {
|
+[](Arguments& args) {
|
||||||
string executable_filename = args.get<string>("executable", true);
|
string executable_filename = args.get<string>("executable", true);
|
||||||
string executable_data = load_file(executable_filename);
|
string executable_data = load_file(executable_filename);
|
||||||
@@ -785,14 +785,14 @@ Action a_decrypt_dcv2_executable(
|
|||||||
});
|
});
|
||||||
Action a_encrypt_dcv2_executable(
|
Action a_encrypt_dcv2_executable(
|
||||||
"encrypt-dcv2-executable", "\
|
"encrypt-dcv2-executable", "\
|
||||||
decrypt-dcv2-executable --executable=EXEC --indexes=INDEXES\n\
|
encrypt-dcv2-executable --executable=EXEC --indexes=INDEXES\n\
|
||||||
decrypt-dcv2-executable --executable=EXEC --simple --seed=SEED\n\
|
encrypt-dcv2-executable --executable=EXEC --simple --seed=SEED\n\
|
||||||
Encrypt a PSO DC v2 executable file. EXEC should be the path to the\n\
|
Encrypt a PSO DC v2 executable file. EXEC should be the path to the\n\
|
||||||
executable (DP_ADDRESS.JPN) and INDEXES should be the path to the index\n\
|
executable (DP_ADDRESS.JPN) and INDEXES should be the path to the index\n\
|
||||||
fixup table (KATSUO.SEA). The output is written to EXEC.enc and\n\
|
fixup table (KATSUO.SEA). The output is written to EXEC.enc and\n\
|
||||||
INDEXES.enc.\n\
|
INDEXES.enc.\n\
|
||||||
If --simple is given, uses the simpler encryption method used in some\n\
|
If --simple is given, uses the simpler encryption method used in some\n\
|
||||||
community modifications of the game. In this case, --seed is required.",
|
community modifications of the game. In this case, --seed is required.\n",
|
||||||
+[](Arguments& args) {
|
+[](Arguments& args) {
|
||||||
string executable_filename = args.get<string>("executable", true);
|
string executable_filename = args.get<string>("executable", true);
|
||||||
string executable_data = load_file(executable_filename);
|
string executable_data = load_file(executable_filename);
|
||||||
@@ -2426,6 +2426,45 @@ Action a_replay_ep3_battle_commands(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Action a_replay_ep3_battle_record(
|
||||||
|
"replay-ep3-battle-record", nullptr, +[](Arguments& args) {
|
||||||
|
auto rec = make_shared<Episode3::BattleRecord>(read_input_data(args));
|
||||||
|
|
||||||
|
auto s = make_shared<ServerState>(get_config_filename(args));
|
||||||
|
s->load_ep3_cards(false);
|
||||||
|
s->load_ep3_maps(false);
|
||||||
|
|
||||||
|
bool is_trial = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3_NTE);
|
||||||
|
|
||||||
|
Episode3::Server::Options options = {
|
||||||
|
.card_index = s->ep3_card_index,
|
||||||
|
.map_index = s->ep3_map_index,
|
||||||
|
.behavior_flags = (Episode3::BehaviorFlag::IGNORE_CARD_COUNTS |
|
||||||
|
Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES |
|
||||||
|
Episode3::BehaviorFlag::DISABLE_MASKING |
|
||||||
|
Episode3::BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING),
|
||||||
|
.opt_rand_stream = make_shared<StringReader>(rec->get_random_stream()),
|
||||||
|
.tournament = nullptr,
|
||||||
|
.trap_card_ids = {},
|
||||||
|
};
|
||||||
|
if (is_trial) {
|
||||||
|
options.behavior_flags |= Episode3::BehaviorFlag::IS_TRIAL_EDITION;
|
||||||
|
}
|
||||||
|
options.behavior_flags |= Episode3::BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING;
|
||||||
|
auto server = make_shared<Episode3::Server>(nullptr, std::move(options));
|
||||||
|
server->init();
|
||||||
|
for (const auto& command : rec->get_all_server_data_commands()) {
|
||||||
|
log_info("Server data command");
|
||||||
|
print_data(stderr, command, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
|
server->on_server_data_input(nullptr, command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Action a_disassemble_ep3_battle_record(
|
||||||
|
"disassemble-ep3-battle-record", nullptr, +[](Arguments& args) {
|
||||||
|
Episode3::BattleRecord(read_input_data(args)).print(stdout);
|
||||||
|
});
|
||||||
|
|
||||||
Action a_run_server_replay_log(
|
Action a_run_server_replay_log(
|
||||||
"", nullptr, +[](Arguments& args) {
|
"", nullptr, +[](Arguments& args) {
|
||||||
{
|
{
|
||||||
|
|||||||
+13
-9
@@ -1588,16 +1588,20 @@ static void on_CA_Ep3(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
|||||||
|
|
||||||
bool battle_finished_before = l->ep3_server->battle_finished;
|
bool battle_finished_before = l->ep3_server->battle_finished;
|
||||||
|
|
||||||
try {
|
if (s->catch_handler_exceptions) {
|
||||||
l->ep3_server->on_server_data_input(c, data);
|
try {
|
||||||
} catch (const exception& e) {
|
l->ep3_server->on_server_data_input(c, data);
|
||||||
c->log.error("Episode 3 engine returned an error: %s", e.what());
|
} catch (const exception& e) {
|
||||||
if (l->battle_record) {
|
c->log.error("Episode 3 engine returned an error: %s", e.what());
|
||||||
string filename = string_printf("system/ep3/battle-records/exc.%" PRIu64 ".mzrd", now());
|
if (l->battle_record) {
|
||||||
save_file(filename, l->battle_record->serialize());
|
string filename = string_printf("system/ep3/battle-records/exc.%" PRIu64 ".mzrd", now());
|
||||||
c->log.error("Saved partial battle record as %s", filename.c_str());
|
save_file(filename, l->battle_record->serialize());
|
||||||
|
c->log.error("Saved partial battle record as %s", filename.c_str());
|
||||||
|
}
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
throw;
|
} else {
|
||||||
|
l->ep3_server->on_server_data_input(c, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the battle has finished, finalize the recording and link it to all
|
// If the battle has finished, finalize the recording and link it to all
|
||||||
|
|||||||
Reference in New Issue
Block a user