fix resident game relaunch from hub

This commit is contained in:
bQUARKz 2026-07-04 18:26:25 +01:00
parent a517a24978
commit e19409a2ef
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 111 additions and 1 deletions

View File

@ -96,9 +96,14 @@ impl Firmware {
signals: &InputSignals,
platform: &mut dyn RuntimePlatform,
) {
let entering_game_from_home = matches!(self.state, FirmwareState::HubHome(_))
&& matches!(new_state, FirmwareState::GameRunning(_));
self.on_exit(signals, platform);
self.state = new_state;
self.state_initialized = false;
if entering_game_from_home {
self.game_input_barrier_frames = self.game_input_barrier_frames.max(1);
}
// Enter the new state immediately to avoid "empty" frames during transitions.
self.on_enter(signals, platform);
@ -620,6 +625,60 @@ mod tests {
assert_eq!(firmware.os.windows().window_count(), 0);
}
#[test]
fn hub_home_clicking_resident_game_resumes_same_task_without_reloading() {
let root = TestGameRoot::new();
root.add_game("stress", true);
let (mut firmware, mut platform) = hub_home_firmware();
firmware.hub.set_game_library(discover_games_root(&root.path));
firmware.tick(&InputSignals::default(), &mut platform);
let launch_click =
InputSignals { f_signal: true, x_pos: 72, y_pos: 90, ..Default::default() };
firmware.tick(&launch_click, &mut platform);
firmware.tick(&InputSignals::default(), &mut platform);
let game_task = match &firmware.state {
FirmwareState::GameRunning(step) => step.task_id,
other => panic!("expected GameRunning state, got {:?}", other),
};
firmware.request_home_from_host();
firmware.tick(&InputSignals::default(), &mut platform);
assert!(matches!(firmware.state, FirmwareState::HubHome(_)));
assert_eq!(firmware.os.lifecycle().resident_game_task(), Some(game_task));
assert_eq!(firmware.os.lifecycle().task_state(game_task), Some(TaskState::Suspended));
firmware.tick(&InputSignals::default(), &mut platform);
firmware.tick(&launch_click, &mut platform);
match &firmware.state {
FirmwareState::GameRunning(step) => assert_eq!(step.task_id, game_task),
other => panic!("expected resident GameRunning state, got {:?}", other),
}
assert_eq!(firmware.os.lifecycle().resident_game_task(), Some(game_task));
assert_eq!(firmware.os.lifecycle().foreground_owner(), ForegroundOwner::Game(game_task));
assert_eq!(firmware.os.lifecycle().task_state(game_task), Some(TaskState::Foreground));
assert_eq!(
firmware.os.lifecycle().pending_game_lifecycle_events().last().map(|event| event.kind),
Some(GameLifecycleEventKind::ResumeForeground)
);
let held_click =
InputSignals { f_signal: true, x_pos: 72, y_pos: 90, ..Default::default() };
firmware.tick(&held_click, &mut platform);
assert!(!platform.input().pad().any());
assert!(!platform.input().touch().f().down);
assert!(firmware.os.lifecycle().pending_game_lifecycle_events().is_empty());
assert_eq!(
firmware.os.vm().delivered_game_lifecycle_events().last().map(|event| event.kind),
Some(GameLifecycleEventKind::ResumeForeground)
);
}
#[test]
fn invalid_games_root_candidate_is_omitted_from_home_launch_boundary() {
let root = TestGameRoot::new();

View File

@ -1,7 +1,9 @@
use crate::firmware::firmware_state::{
AppCrashesStep, FirmwareState, LoadCartridgeStep, ShellRunningStep,
AppCrashesStep, FirmwareState, GameRunningStep, LoadCartridgeStep, ShellRunningStep,
};
use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::InputSignals;
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::cartridge_loader::CartridgeLoader;
use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::primitives::Rect;
@ -38,6 +40,55 @@ impl HubHomeStep {
}
Some(SystemProfileAction::LaunchGame { path }) => match CartridgeLoader::load(&path) {
Ok(cartridge) => {
if cartridge.app_mode == AppMode::Game
&& let Some(task_id) = ctx.os.lifecycle().resident_game_task()
{
if ctx.os.vm().current_app_id() == cartridge.app_id {
match ctx.os.lifecycle().resume_task(task_id) {
Ok(()) => {
ctx.os
.vm()
.transition_render_owner(AppMode::Game, cartridge.app_id);
ctx.platform
.input_mut()
.pad_mut()
.begin_frame(&InputSignals::default());
ctx.platform
.input_mut()
.touch_mut()
.begin_frame(&InputSignals::default());
return Some(FirmwareState::GameRunning(GameRunningStep::new(
task_id,
)));
}
Err(error) => {
ctx.os.log(
LogLevel::Warn,
LogSource::Hub,
0,
format!(
"Failed to resume resident Home game {}: {error:?}",
cartridge.title
),
);
return None;
}
}
}
let resident_app_id = ctx.os.vm().current_app_id();
ctx.os.log(
LogLevel::Warn,
LogSource::Hub,
0,
format!(
"Cannot launch Home game {} while resident game app_id {} is active",
cartridge.title, resident_app_id
),
);
return None;
}
return Some(FirmwareState::LoadCartridge(LoadCartridgeStep::new(cartridge)));
}
Err(error) => {