Skip to content

Commit bd9484a

Browse files
Mivikclaude
andcommitted
fix(hykb): tear down session on async account switch
After login the SDK may raise its own dialog (real-name / anti-addiction gate) offering "switch account". Picking it fires onSwitchUser, which the native side forwards as hykbLoginCallback(0, newUid, ...) — but with no login in flight HYKB_TX is empty, so the code==0 callback fell through the 2005 arm and was silently dropped. The SDK ended up on the new account while the in-game session still belonged to the old one, leaving Phira's identity out of sync with the SDK. Handle the request-less code==0 callback: record the switched uid (off the JNI thread, since DATA is unsynchronized) and, on the game thread in the_main, drop the session when it no longer matches the current me.hykb_uid. HomePage's per-frame login.force then re-runs the login flow as the new account. The SDK is left signed into the new account, so no hykb_logout re-entry. The uid comparison also ignores the startup onLoginSucceed auto-login, which arrives as the same code==0. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bbf9db0 commit bd9484a

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

phira/src/lib.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,20 @@ async fn the_main() -> Result<()> {
241241
fps_time_sum += frame_time;
242242
}
243243
last_frame_start = frame_start as f32;
244+
// The SDK switched HYKB accounts underneath us (see `hykbLoginCallback`).
245+
// If the freshly signed-in uid no longer matches the account the in-game
246+
// session is bound to, drop the session so `HomePage` forces a re-login
247+
// as the new account. The SDK is left alone (already on the new account),
248+
// so this only clears local tokens — no `hykb_logout` re-entry.
249+
#[cfg(feature = "hykb")]
250+
if let Some(uid) = HYKB_SWITCH_UID.lock().unwrap().take() {
251+
if get_data().me.as_ref().and_then(|it| it.hykb_uid) != Some(uid) && get_data().me.is_some() {
252+
get_data_mut().me = None;
253+
get_data_mut().tokens = None;
254+
let _ = save_data();
255+
sync_data();
256+
}
257+
}
244258
let res = || -> Result<()> {
245259
main.update()?;
246260
main.render(&mut painter)?;
@@ -428,6 +442,13 @@ impl HykbCredential {
428442
/// Slot for the pending HYKB login result. The native callback fulfills it.
429443
static HYKB_TX: Mutex<Option<tokio::sync::oneshot::Sender<HykbCredential>>> = Mutex::new(None);
430444

445+
/// uid the SDK reported switching to via an async, request-less `code == 0`
446+
/// callback (the player picked "switch account" from the SDK's own dialog).
447+
/// The native callback thread only records it here; the teardown itself runs on
448+
/// the game thread in `the_main` to avoid racing the unsynchronized `DATA`.
449+
#[cfg(feature = "hykb")]
450+
static HYKB_SWITCH_UID: Mutex<Option<i64>> = Mutex::new(None);
451+
431452
/// Call a no-arg `void` method on the Android host activity (the HYKB shell).
432453
#[cfg(all(target_os = "android", feature = "hykb"))]
433454
fn call_activity_void(method: &'static jni::strings::JNIStr) {
@@ -539,6 +560,17 @@ pub extern "C" fn Java_quad_1native_QuadNative_hykbLoginCallback(
539560
// Other async codes (e.g. 2008 "continue playing") are handled inside
540561
// the SDK and need no response here.
541562
std::process::exit(0);
563+
} else if code == 0 {
564+
// A request-less success: the SDK switched accounts on its own (the
565+
// player chose "switch account" from a dialog it raised after login,
566+
// e.g. the real-name / anti-addiction gate). The in-game session still
567+
// belongs to the previous account, so record the new uid; the game
568+
// thread compares it against the current session and tears it down on a
569+
// mismatch. Done off the JNI thread because `DATA` is unsynchronized.
570+
#[cfg(feature = "hykb")]
571+
{
572+
*HYKB_SWITCH_UID.lock().unwrap() = Some(uid as i64);
573+
}
542574
}
543575
}
544576

0 commit comments

Comments
 (0)