Skip to content

Commit

Permalink
Rewrite timer state update (#71)
Browse files Browse the repository at this point in the history
* Don't reset on manual Ended

* timer_state may be out of date

* Rewrite timer state update

* don't forget to update last_state before return

* refactor action Split
  • Loading branch information
AlexKnauth authored Apr 24, 2024
1 parent 440c6d6 commit a50dfb8
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 152 deletions.
150 changes: 42 additions & 108 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ mod hollow_knight_memory;
mod legacy_xml;
mod settings_gui;
mod splits;
mod timer;

use asr::future::{next_tick, retry};
use asr::Process;
use asr::time::Duration;
use asr::timer::TimerState;
use settings_gui::{SettingsGui, TimingMethod};
use hollow_knight_memory::*;
use splits::{Split, SplitterAction};
use splits::Split;
use timer::{Resettable, SplitterAction, Timer};
use ugly_widget::store::StoreGui;

asr::async_main!(stable);
Expand Down Expand Up @@ -47,7 +49,6 @@ async fn main() {
// TODO: Load some initial information from the process.
let mut scene_store = Box::new(SceneStore::new());
let mut load_remover = Box::new(TimingMethodLoadRemover::new(timing_method));
let mut auto_reset: &'static [TimerState] = splits::auto_reset_safe(&splits);

next_tick().await;
let game_manager_finder = Box::new(GameManagerFinder::wait_attach(&process).await);
Expand All @@ -62,12 +63,11 @@ async fn main() {
#[cfg(debug_assertions)]
asr::print_message(&format!("scenes_grub_rescued: {:?}", scenes_grub_rescued));

let mut last_timer_state = TimerState::Unknown;
let mut i = 0;
let mut timer = Timer::new(splits.len(), splits::auto_reset_safe(&splits));
loop {
tick_action(&process, &splits, &mut last_timer_state, &mut i, auto_reset, &game_manager_finder, &mut scene_store, &mut player_data_store, &mut scene_data_store, &mut load_remover).await;
tick_action(&process, &splits, &mut timer, &game_manager_finder, &mut scene_store, &mut player_data_store, &mut scene_data_store, &mut load_remover).await;

load_remover.load_removal(&process, &game_manager_finder, i);
load_remover.load_removal(&process, &game_manager_finder);

#[cfg(debug_assertions)]
let new_scenes_grub_rescued = game_manager_finder.scenes_grub_rescued(&process);
Expand All @@ -79,13 +79,13 @@ async fn main() {

ticks_since_gui += 1;
if TICKS_PER_GUI <= ticks_since_gui && gui.load_update_store_if_unchanged() {
if i == 0 && [TimerState::NotRunning, TimerState::Ended].contains(&asr::timer::state()) {
if timer.is_timer_state_between_runs() {
if let Some(new_timing_method) = gui.check_timing_method(&mut timing_method) {
*load_remover = TimingMethodLoadRemover::new(new_timing_method);
}
}
if let Some(new_splits) = gui.check_splits(&mut splits) {
auto_reset = splits::auto_reset_safe(new_splits);
timer.renew(new_splits.len(), splits::auto_reset_safe(new_splits));
}
ticks_since_gui = 0;
}
Expand All @@ -109,65 +109,39 @@ async fn wait_attach_hollow_knight(gui: &mut SettingsGui, timing_method: &mut Ti
async fn tick_action(
process: &Process,
splits: &[splits::Split],
last_timer_state: &mut TimerState,
i: &mut usize,
auto_reset: &'static [TimerState],
timer: &mut Timer,
game_manager_finder: &GameManagerFinder,
scene_store: &mut SceneStore,
player_data_store: &mut PlayerDataStore,
scene_data_store: &mut SceneDataStore,
load_remover: &mut TimingMethodLoadRemover,
) {
let n = splits.len();
let timer_state = asr::timer::state();
match timer_state {
// detect manual resets
TimerState::NotRunning if 0 < *i => {
*i = 0;
load_remover.reset();
asr::print_message("Detected a manual reset.");
}
// detect manual starts
TimerState::Running if *i == 0 && is_timer_state_between_runs(*last_timer_state) => {
*i = 1;
load_remover.reset();
asr::print_message("Detected a manual start.");
}
// detect manual end-splits
TimerState::Ended if 0 < *i && *i < n => {
*i = n;
load_remover.reset();
asr::print_message("Detected a manual end-split.");
}
s => {
*last_timer_state = s;
}
}
timer.update(load_remover);

let trans_now = scene_store.transition_now(&process, &game_manager_finder);
loop {
let Some(s) = splits.get(*i) else {
let Some(s) = splits.get(timer.i()) else {
break;
};
let a = splits::splits(s, &process, game_manager_finder, trans_now, scene_store, player_data_store, scene_data_store);
match a {
SplitterAction::Split | SplitterAction::ManualSplit => {
splitter_action(a, i, n, load_remover);
timer.action(a, load_remover);
next_tick().await;
break;
}
SplitterAction::Skip | SplitterAction::Reset => {
splitter_action(a, i, n, load_remover);
timer.action(a, load_remover);
next_tick().await;
// no break, allow other actions after a skip or reset
}
SplitterAction::Pass => {
if auto_reset.contains(&timer_state) {
if timer.is_auto_reset_safe() {
let a0 = splits::splits(&splits[0], &process, game_manager_finder, trans_now, scene_store, player_data_store, scene_data_store);
match a0 {
SplitterAction::Split | SplitterAction::Reset => {
*i = 0;
splitter_action(a0, i, n, load_remover);
timer.reset();
timer.action(a0, load_remover);
}
_ => (),
}
Expand All @@ -177,10 +151,6 @@ async fn tick_action(
}
}

if auto_reset.contains(&timer_state) && n <= *i {
*i = 0;
}

if trans_now {
if scene_store.pair().old == MENU_TITLE {
player_data_store.reset();
Expand All @@ -191,45 +161,20 @@ async fn tick_action(
}
}

fn is_timer_state_between_runs(s: TimerState) -> bool {
s == TimerState::NotRunning || s == TimerState::Ended
enum TimingMethodLoadRemover {
LoadRemover(LoadRemover),
HitCounter(HitCounter),
}

fn splitter_action(a: SplitterAction, i: &mut usize, n: usize, load_remover: &mut TimingMethodLoadRemover) {
match a {
SplitterAction::Pass => (),
SplitterAction::Reset => {
asr::timer::reset();
load_remover.reset();
*i = 0;
}
SplitterAction::Skip => {
asr::timer::skip_split();
*i += 1;
}
SplitterAction::Split if *i == 0 => {
asr::timer::reset();
asr::timer::start();
load_remover.reset();
*i += 1;
}
SplitterAction::Split => {
asr::timer::split();
*i += 1;
}
SplitterAction::ManualSplit => {
if 0 < *i && *i + 1 < n {
*i += 1;
}
impl Resettable for TimingMethodLoadRemover {
fn reset(&mut self) {
match self {
TimingMethodLoadRemover::LoadRemover(lr) => lr.reset(),
TimingMethodLoadRemover::HitCounter(hc) => hc.reset(),
}
}
}

enum TimingMethodLoadRemover {
LoadRemover(LoadRemover),
HitCounter(HitCounter),
}

impl TimingMethodLoadRemover {
fn new(timing_method: TimingMethod) -> TimingMethodLoadRemover {
match timing_method {
Expand All @@ -239,17 +184,10 @@ impl TimingMethodLoadRemover {
}
}

fn reset(&mut self) {
fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder) -> Option<()> {
match self {
TimingMethodLoadRemover::LoadRemover(lr) => lr.reset(),
TimingMethodLoadRemover::HitCounter(hc) => hc.reset(),
}
}

fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder, i: usize) -> Option<()> {
match self {
TimingMethodLoadRemover::LoadRemover(lr) => lr.load_removal(process, game_manager_finder, i),
TimingMethodLoadRemover::HitCounter(hc) => hc.load_removal(process, game_manager_finder, i),
TimingMethodLoadRemover::LoadRemover(lr) => lr.load_removal(process, game_manager_finder),
TimingMethodLoadRemover::HitCounter(hc) => hc.load_removal(process, game_manager_finder),
}
}
}
Expand All @@ -261,6 +199,10 @@ struct LoadRemover {
last_paused: bool,
}

impl Resettable for LoadRemover {
fn reset(&mut self) {}
}

#[allow(unused)]
impl LoadRemover {
fn new() -> LoadRemover {
Expand All @@ -272,9 +214,7 @@ impl LoadRemover {
}
}

fn reset(&mut self) {}

fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder, _i: usize) -> Option<()> {
fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder) -> Option<()> {
// Initialize pointers for load-remover before timer is running
let maybe_ui_state = game_manager_finder.get_ui_state(process);
let maybe_scene_name = game_manager_finder.get_scene_name(process);
Expand Down Expand Up @@ -355,7 +295,15 @@ struct HitCounter {
last_hazard: bool,
last_dead_or_0: bool,
last_exiting_level: Option<String>,
last_index: usize,
}

impl Resettable for HitCounter {
fn reset(&mut self) {
self.hits = 0;
asr::timer::pause_game_time();
asr::timer::set_game_time(Duration::seconds(0));
asr::timer::set_variable_int("hits", 0);
}
}

#[allow(unused)]
Expand All @@ -369,27 +317,13 @@ impl HitCounter {
last_hazard: false,
last_dead_or_0: false,
last_exiting_level: None,
last_index: 0,
}
}

fn reset(&mut self) {
self.hits = 0;
asr::timer::pause_game_time();
asr::timer::set_game_time(Duration::seconds(0));
asr::timer::set_variable_int("hits", 0);
}

fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder, i: usize) -> Option<()> {
fn load_removal(&mut self, process: &Process, game_manager_finder: &GameManagerFinder) -> Option<()> {

asr::timer::pause_game_time();

// detect resets
if i == 0 && 0 < self.last_index {
self.reset();
}
self.last_index = i;

// only count hits if timer is running
if asr::timer::state() != TimerState::Running { return Some(()); }

Expand Down
46 changes: 2 additions & 44 deletions src/splits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,8 @@ use serde::{Deserialize, Serialize};
use ugly_widget::radio_button::{RadioButtonOptions, options_str};
use ugly_widget::store::StoreWidget;

use super::hollow_knight_memory::*;

#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum SplitterAction {
#[default]
Pass,
Split,
Skip,
Reset,
ManualSplit,
}

impl SplitterAction {
pub fn or_else<F: FnOnce() -> SplitterAction>(self, f: F) -> SplitterAction {
match self {
SplitterAction::Pass => f(),
a => a,
}
}
}

fn should_split(b: bool) -> SplitterAction {
if b {
SplitterAction::Split
} else {
SplitterAction::Pass
}
}

fn should_skip(b: bool) -> SplitterAction {
if b {
SplitterAction::Skip
} else {
SplitterAction::Pass
}
}

fn should_split_skip(mb: Option<bool>) -> SplitterAction {
match mb {
Some(true) => SplitterAction::Split,
Some(false) => SplitterAction::Skip,
None => SplitterAction::Pass,
}
}
use crate::hollow_knight_memory::*;
use crate::timer::*;

#[derive(Clone, Debug, Default, Deserialize, Eq, Gui, Ord, PartialEq, PartialOrd, RadioButtonOptions, Serialize)]
pub enum Split {
Expand Down
Loading

0 comments on commit a50dfb8

Please sign in to comment.