wait for responses before sending chains of function calls

This commit is contained in:
Martin Michelsen
2023-06-07 00:32:46 -07:00
parent e1f584984f
commit d60404ff0a
6 changed files with 104 additions and 73 deletions
+36 -35
View File
@@ -260,27 +260,21 @@ static void proxy_command_auction(shared_ptr<ServerState>,
static void server_command_patch(shared_ptr<ServerState> s, shared_ptr<Lobby>,
shared_ptr<Client> c, const std::u16string& args) {
string basename = encode_sjis(args);
auto send_call = [s, basename, wc = weak_ptr<Client>(c)]() {
auto c = wc.lock();
if (!c) {
return;
}
try {
try {
prepare_client_for_patches(s, c, [s, wc = weak_ptr<Client>(c), basename]() {
auto c = wc.lock();
if (!c) {
return;
}
// Note: We can't look this up outside of the closure because
// c->specific_version can change during prepare_client_for_patches
auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at(
string_printf("%s-%08" PRIX32, basename.c_str(), c->specific_version));
send_function_call(c, fn);
} catch (const out_of_range&) {
send_text_message(c, u"Invalid patch name");
}
};
send_cache_patch_if_needed(s, c);
if (c->version() == GameVersion::GC &&
c->specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
send_function_call(c, s->function_code_index->name_to_function.at("VersionDetect"));
c->on_version_detect_response = send_call;
} else {
send_call();
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
});
} catch (const out_of_range&) {
send_text_message(c, u"Invalid patch name");
}
}
@@ -307,26 +301,33 @@ static void proxy_command_patch(shared_ptr<ServerState> s,
}
};
// This mirrors the implementation in send_cache_patch_if_needed
auto send_version_detect_or_send_call = [s, basename, &session, send_call]() {
if (session.version == GameVersion::GC &&
session.newserv_client_config.cfg.specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
send_function_call(
session.client_channel,
session.newserv_client_config.cfg.flags,
s->function_code_index->name_to_function.at("VersionDetect"));
session.function_call_return_handler_queue.emplace_back(send_call);
} else {
send_call(session.newserv_client_config.cfg.specific_version, 0);
}
};
// This mirrors the implementation in prepare_client_for_patches
if (!(session.newserv_client_config.cfg.flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
send_function_call(
session.client_channel, session.newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC);
send_function_call(
session.client_channel, session.newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
session.function_call_return_handler_queue.emplace_back(empty_patch_return_handler);
session.function_call_return_handler_queue.emplace_back(empty_patch_return_handler);
session.newserv_client_config.cfg.flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
}
if (session.version == GameVersion::GC &&
session.newserv_client_config.cfg.specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
send_function_call(
session.client_channel,
session.newserv_client_config.cfg.flags,
s->function_code_index->name_to_function.at("VersionDetect"));
session.function_call_return_handler_queue.emplace_back(send_call);
session.client_channel, session.newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
session.function_call_return_handler_queue.emplace_back([s, session_p = &session, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
send_function_call(
session_p->client_channel, session_p->newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
session_p->function_call_return_handler_queue.emplace_back([session_p, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
session_p->newserv_client_config.cfg.flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
send_version_detect_or_send_call();
});
});
} else {
send_call(session.newserv_client_config.cfg.specific_version, 0);
send_version_detect_or_send_call();
}
}
+1 -1
View File
@@ -156,7 +156,7 @@ struct Client {
bool can_chat;
std::string pending_bb_save_username;
uint8_t pending_bb_save_player_index;
std::function<void()> on_version_detect_response;
std::deque<std::function<void(uint32_t, uint32_t)>> function_call_response_queue;
// File loading state
uint32_t dol_base_addr;
+7
View File
@@ -72,6 +72,13 @@ string CompiledFunctionCode::generate_client_command_t(
w.put_u8(0);
}
footer.relocations_offset = w.size();
// Always write at least 4 bytes even if there are no relocations
if (this->relocation_deltas.empty()) {
w.put_u32(0);
}
if (override_relocations_offset) {
footer.relocations_offset = override_relocations_offset;
} else {
+15 -28
View File
@@ -1734,19 +1734,9 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (c->flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw runtime_error("client does not support send_function_call");
}
send_cache_patch_if_needed(s, c);
if (c->version() == GameVersion::GC &&
c->specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
send_function_call(c, s->function_code_index->name_to_function.at("VersionDetect"));
c->on_version_detect_response = [s, wc = weak_ptr<Client>(c)]() {
auto c = wc.lock();
if (c) {
send_menu(c, s->function_code_index->patch_menu(c->specific_version));
}
};
} else {
prepare_client_for_patches(s, c, [s, c]() -> void {
send_menu(c, s->function_code_index->patch_menu(c->specific_version));
}
});
break;
case MainMenuItemID::PROGRAMS:
@@ -1756,8 +1746,9 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (c->flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw runtime_error("client does not support send_function_call");
}
send_cache_patch_if_needed(s, c);
send_menu(c, s->dol_file_index->menu);
prepare_client_for_patches(s, c, [s, c]() -> void {
send_menu(c, s->dol_file_index->menu);
});
break;
case MainMenuItemID::DISCONNECT:
@@ -2071,6 +2062,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint64_t key = (static_cast<uint64_t>(item_id) << 32) | c->specific_version;
send_function_call(
c, s->function_code_index->menu_item_id_and_specific_version_to_patch_function.at(key));
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
send_menu(c, s->function_code_index->patch_menu(c->specific_version));
}
break;
@@ -2287,22 +2279,13 @@ static void send_dol_file_chunk(shared_ptr<ServerState> s, shared_ptr<Client> c,
static void on_B3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t flag, const string& data) {
const auto& cmd = check_size_t<C_ExecuteCodeResult_B3>(data);
if (flag == 0) {
return;
}
auto called_fn = s->function_code_index->index_to_function.at(flag);
if (called_fn->name == "VersionDetect") {
// This is sent the first time the client chooses Patches from the main
// menu, so send the Patches menu when we get the response here
c->specific_version = cmd.return_value;
c->log.info("Version detected as %08" PRIX32, c->specific_version);
if (c->on_version_detect_response) {
c->on_version_detect_response();
c->on_version_detect_response = nullptr;
}
if (!c->function_call_response_queue.empty()) {
auto& handler = c->function_call_response_queue.front();
handler(cmd.return_value, cmd.checksum);
c->function_call_response_queue.pop_front();
} else if (c->loading_dol_file.get()) {
auto called_fn = s->function_code_index->index_to_function.at(flag);
if (called_fn->name == "ReadMemoryWord") {
c->dol_base_addr = (cmd.return_value - c->loading_dol_file->data.size()) & (~3);
send_dol_file_chunk(s, c, c->dol_base_addr);
@@ -2318,7 +2301,11 @@ static void on_B3(shared_ptr<ServerState> s, shared_ptr<Client> c,
} else {
send_dol_file_chunk(s, c, cmd.return_value);
}
} else {
throw logic_error("unknown function called during DOL loading");
}
} else {
throw runtime_error("function call response queue is empty, and no program is being sent");
}
}
+41 -7
View File
@@ -311,14 +311,48 @@ void send_quest_buffer_overflow(
send_command_t(c, 0xA7, 0x00, cmd);
}
void send_cache_patch_if_needed(shared_ptr<ServerState> s, shared_ptr<Client> c) {
void empty_function_call_response_handler(uint32_t, uint32_t) {}
void prepare_client_for_patches(shared_ptr<ServerState> s, shared_ptr<Client> c, std::function<void()> on_complete) {
auto send_version_detect = [s, wc = weak_ptr<Client>(c), on_complete]() -> void {
auto c = wc.lock();
if (!c) {
return;
}
if (c->version() == GameVersion::GC &&
c->specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
send_function_call(c, s->function_code_index->name_to_function.at("VersionDetect"));
c->function_call_response_queue.emplace_back([s, c, on_complete](uint32_t specific_version, uint32_t) -> void {
c->specific_version = specific_version;
c->log.info("Version detected as %08" PRIX32, c->specific_version);
on_complete();
});
} else {
on_complete();
}
};
if (!(c->flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC);
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
c->flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
send_update_client_config(c);
send_function_call(c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
c->function_call_response_queue.emplace_back([s, wc = weak_ptr<Client>(c), send_version_detect](uint32_t, uint32_t) -> void {
auto c = wc.lock();
if (!c) {
return;
}
send_function_call(c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
c->function_call_response_queue.emplace_back([s, wc = weak_ptr<Client>(c), send_version_detect](uint32_t, uint32_t) -> void {
auto c = wc.lock();
if (!c) {
return;
}
c->log.info("Client cache behavior patched");
c->flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
send_update_client_config(c);
send_version_detect();
});
});
} else {
send_version_detect();
}
}
+4 -2
View File
@@ -130,10 +130,12 @@ void send_server_init(
uint8_t flags);
void send_update_client_config(std::shared_ptr<Client> c);
void empty_function_call_response_handler(uint32_t, uint32_t);
void send_quest_buffer_overflow(
std::shared_ptr<ServerState> s, std::shared_ptr<Client> c);
void send_cache_patch_if_needed(std::shared_ptr<ServerState> s, std::shared_ptr<Client> c);
uint32_t send_cache_patch_if_needed(std::shared_ptr<ServerState> s, Channel& c, uint32_t flags);
void prepare_client_for_patches(
std::shared_ptr<ServerState> s, std::shared_ptr<Client> c, std::function<void()> on_complete);
void send_function_call(
Channel& ch,
uint64_t client_flags,