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>, static void server_command_patch(shared_ptr<ServerState> s, shared_ptr<Lobby>,
shared_ptr<Client> c, const std::u16string& args) { shared_ptr<Client> c, const std::u16string& args) {
string basename = encode_sjis(args); string basename = encode_sjis(args);
auto send_call = [s, basename, wc = weak_ptr<Client>(c)]() { try {
auto c = wc.lock(); prepare_client_for_patches(s, c, [s, wc = weak_ptr<Client>(c), basename]() {
if (!c) { auto c = wc.lock();
return; if (!c) {
} return;
try { }
// 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( 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)); string_printf("%s-%08" PRIX32, basename.c_str(), c->specific_version));
send_function_call(c, fn); send_function_call(c, fn);
} catch (const out_of_range&) { c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
send_text_message(c, u"Invalid patch name"); });
} } 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();
} }
} }
@@ -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)) { if (!(session.newserv_client_config.cfg.flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
send_function_call( 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); session.client_channel, session.newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
send_function_call( session.function_call_return_handler_queue.emplace_back([s, session_p = &session, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
session.client_channel, session.newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase2")); send_function_call(
session.function_call_return_handler_queue.emplace_back(empty_patch_return_handler); session_p->client_channel, session_p->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_p->function_call_return_handler_queue.emplace_back([session_p, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
session.newserv_client_config.cfg.flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH; session_p->newserv_client_config.cfg.flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
} send_version_detect_or_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 { } 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; bool can_chat;
std::string pending_bb_save_username; std::string pending_bb_save_username;
uint8_t pending_bb_save_player_index; 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 // File loading state
uint32_t dol_base_addr; uint32_t dol_base_addr;
+7
View File
@@ -72,6 +72,13 @@ string CompiledFunctionCode::generate_client_command_t(
w.put_u8(0); 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) { if (override_relocations_offset) {
footer.relocations_offset = override_relocations_offset; footer.relocations_offset = override_relocations_offset;
} else { } 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) { if (c->flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw runtime_error("client does not support send_function_call"); throw runtime_error("client does not support send_function_call");
} }
send_cache_patch_if_needed(s, c); prepare_client_for_patches(s, c, [s, c]() -> void {
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 {
send_menu(c, s->function_code_index->patch_menu(c->specific_version)); send_menu(c, s->function_code_index->patch_menu(c->specific_version));
} });
break; break;
case MainMenuItemID::PROGRAMS: 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) { if (c->flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw runtime_error("client does not support send_function_call"); throw runtime_error("client does not support send_function_call");
} }
send_cache_patch_if_needed(s, c); prepare_client_for_patches(s, c, [s, c]() -> void {
send_menu(c, s->dol_file_index->menu); send_menu(c, s->dol_file_index->menu);
});
break; break;
case MainMenuItemID::DISCONNECT: 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; uint64_t key = (static_cast<uint64_t>(item_id) << 32) | c->specific_version;
send_function_call( send_function_call(
c, s->function_code_index->menu_item_id_and_specific_version_to_patch_function.at(key)); 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)); send_menu(c, s->function_code_index->patch_menu(c->specific_version));
} }
break; 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, static void on_B3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t flag, const string& data) { uint16_t, uint32_t flag, const string& data) {
const auto& cmd = check_size_t<C_ExecuteCodeResult_B3>(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()) { } else if (c->loading_dol_file.get()) {
auto called_fn = s->function_code_index->index_to_function.at(flag);
if (called_fn->name == "ReadMemoryWord") { if (called_fn->name == "ReadMemoryWord") {
c->dol_base_addr = (cmd.return_value - c->loading_dol_file->data.size()) & (~3); c->dol_base_addr = (cmd.return_value - c->loading_dol_file->data.size()) & (~3);
send_dol_file_chunk(s, c, c->dol_base_addr); 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 { } else {
send_dol_file_chunk(s, c, cmd.return_value); 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); 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)) { if (!(c->flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
send_function_call( send_function_call(c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC); c->function_call_response_queue.emplace_back([s, wc = weak_ptr<Client>(c), send_version_detect](uint32_t, uint32_t) -> void {
send_function_call( auto c = wc.lock();
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2")); if (!c) {
c->flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH; return;
send_update_client_config(c); }
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); uint8_t flags);
void send_update_client_config(std::shared_ptr<Client> c); 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( void send_quest_buffer_overflow(
std::shared_ptr<ServerState> s, std::shared_ptr<Client> c); 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); void prepare_client_for_patches(
uint32_t send_cache_patch_if_needed(std::shared_ptr<ServerState> s, Channel& c, uint32_t flags); std::shared_ptr<ServerState> s, std::shared_ptr<Client> c, std::function<void()> on_complete);
void send_function_call( void send_function_call(
Channel& ch, Channel& ch,
uint64_t client_flags, uint64_t client_flags,