wowlab_engine_application/simulate_intent/
handler.rs1use wowlab_engine_ports::{
2 ContentCatalog, DynDataResolver, EngineError, HandlerParams, SpecHandler,
3};
4use wowlab_types::constants::DEFAULT_DURATION_S;
5
6use super::setup::{ResolvedSetup, resolve_setup};
7use crate::{
8 error::{ApplicationError, ApplicationStage, EngineResultExt as _},
9 intent_builder::IntentOverrides,
10 resolved_handler::ResolvedHandler,
11};
12
13pub(super) struct BootstrapResult {
14 pub handler: ResolvedHandler,
15 pub duration_s: f64,
16}
17
18pub fn build_handler_from_setup(
20 setup: &ResolvedSetup,
21) -> Result<ResolvedHandler, ApplicationError> {
22 let handler = (setup.descriptor.handler_factory)(
23 HandlerParams {
24 game_data: setup.game_data.clone(),
25 rotation: &setup.rotation,
26 stats: &setup.stats,
27 talent_selections: &setup.talent_selections,
28 encounter: &setup.encounter,
29 fight_duration_secs: setup.encounter.fight_duration_secs(),
30 equipped_items: &setup.equipped_items,
31 set_bonus_auras: &setup.set_bonus_auras,
32 bloodlust: setup.consumables.bloodlust,
33 pre_pot_tempered: setup.consumables.pre_pot_tempered,
34 flask: setup.consumables.flask,
35 augment_rune: setup.consumables.augment_rune,
36 race: setup.race,
37 weapon_enchant_procs: &setup.weapon_enchant_procs,
38 bugs: setup.bugs.clone(),
39 cast_latency: setup.cast_latency,
40 raid_events: &setup.raid_events,
41 external_buffs: &setup.external_buffs,
42 }
43 .validate()
44 .in_application_stage(ApplicationStage::HandlerConstruction)?,
45 )
46 .in_application_stage(ApplicationStage::HandlerConstruction)?;
47
48 Ok(ResolvedHandler::from_boxed(handler))
49}
50
51pub(super) async fn bootstrap(
52 catalog: &ContentCatalog,
53 sim_config: &str,
54 resolver: &DynDataResolver<'_>,
55 overrides: &IntentOverrides,
56) -> Result<BootstrapResult, ApplicationError> {
57 let setup = resolve_setup(catalog, sim_config, resolver, overrides).await?;
58 let duration_s = setup.duration_s;
59 let handler = build_handler_from_setup(&setup)?;
60
61 Ok(BootstrapResult {
62 handler,
63 duration_s,
64 })
65}
66
67pub(crate) fn run_on_sim_start(handler: &mut dyn SpecHandler) {
68 let sim_state = wowlab_engine_ports::SimState::new(
69 0,
70 wowlab_types::sim::SimTime::from_secs(wowlab_types::numeric::f64_to_u32_saturating_round(
71 DEFAULT_DURATION_S,
72 )),
73 0,
74 );
75 let mut sink = wowlab_engine_telemetry::TelemetrySink::new();
76 let mut ctx = wowlab_engine_ports::SimContext {
77 state: &sim_state,
78 telemetry: &mut sink,
79 };
80
81 handler.on_sim_start(&mut ctx);
82}
83
84pub async fn build_handler(
86 catalog: &ContentCatalog,
87 sim_config: &str,
88 resolver: &DynDataResolver<'_>,
89) -> Result<ResolvedHandler, ApplicationError> {
90 let mut handler = bootstrap(
91 catalog,
92 sim_config,
93 resolver,
94 &IntentOverrides {
95 skip_rotation: true,
96 ..Default::default()
97 },
98 )
99 .await?
100 .handler;
101
102 run_on_sim_start(handler.handler_mut());
103
104 Ok(handler)
105}
106
107pub(super) fn handler_factory(
108 handler: ResolvedHandler,
109) -> impl Fn() -> Result<ResolvedHandler, ApplicationError> {
110 let cell = std::cell::Cell::new(Some(handler));
111
112 move || {
113 cell.take().ok_or_else(|| {
114 ApplicationError::from_engine(
115 ApplicationStage::HandlerConstruction,
116 EngineError::spec_construction("handler factory called more than once"),
117 )
118 })
119 }
120}