wait for responses before sending chains of function calls
This commit is contained in:
+36
-35
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user