Skip to main content

wowlab_engine_application/simulate_intent/
entry.rs

1use wowlab_engine_ports::{
2    ChunkAssignment, ChunkReport, ContentCatalog, DataResolver, DynDataResolver, ProgressSink,
3};
4use wowlab_types::game::SpecId;
5
6use super::handler::{bootstrap, handler_factory};
7use crate::{
8    chunk_executor::{run_chunk, run_chunk_with_trace},
9    error::{ApplicationError, ApplicationStage, EngineResultExt as _},
10    intent_builder::{IntentOverrides, SimRequest},
11};
12
13/// Rotation-script id under which a spec's bundled "assisted" priority list is registered.
14#[must_use]
15pub fn assisted_rotation_id(spec: SpecId) -> String {
16    format!("{}_assisted", spec.slug())
17}
18
19/// Resolve a spec's assisted rotation script from the resolver, keyed by [`assisted_rotation_id`].
20pub async fn resolve_assisted_rotation_script(
21    spec: SpecId,
22    resolver: &DynDataResolver<'_>,
23) -> Result<String, ApplicationError> {
24    let id = assisted_rotation_id(spec);
25
26    resolver
27        .get_rotation_script(&id)
28        .await
29        .in_application_stage(ApplicationStage::AssistedRotationResolution)
30}
31
32async fn bootstrap_chunk(
33    catalog: &ContentCatalog,
34    sim_config: &str,
35    chunk: &ChunkAssignment,
36    seed_base: u64,
37    resolver: &DynDataResolver<'_>,
38    overrides: &IntentOverrides,
39) -> Result<(super::handler::BootstrapResult, u64), ApplicationError> {
40    let bootstrapped = bootstrap(catalog, sim_config, resolver, overrides).await?;
41    let effective_seed = seed_base.wrapping_add(chunk.seed_offset);
42
43    Ok((bootstrapped, effective_seed))
44}
45
46/// Canonical entry point taking a [`SimRequest`] envelope.
47pub async fn simulate_intent_request(
48    catalog: &ContentCatalog,
49    request: SimRequest<'_>,
50    resolver: &DynDataResolver<'_>,
51    progress: &dyn ProgressSink,
52) -> Result<ChunkReport, ApplicationError> {
53    let (bootstrapped, effective_seed) = bootstrap_chunk(
54        catalog,
55        request.sim_config,
56        request.chunk,
57        request.seed_base,
58        resolver,
59        &request.overrides,
60    )
61    .await?;
62
63    run_chunk(
64        crate::ChunkRun {
65            assignment: request.chunk,
66            duration_s: bootstrapped.duration_s,
67            seed_base: effective_seed,
68        },
69        progress,
70        handler_factory(bootstrapped.handler),
71    )
72}
73
74/// Run a sim from a TOML config with the canonical raid profile.
75// docref:start overview-arch-simulate-intent
76pub async fn simulate_intent(
77    catalog: &ContentCatalog,
78    sim_config: &str,
79    chunk: &ChunkAssignment,
80    seed_base: u64,
81    resolver: &DynDataResolver<'_>,
82    progress: &dyn ProgressSink,
83) -> Result<ChunkReport, ApplicationError> {
84    // docref:end overview-arch-simulate-intent
85    let request = SimRequest {
86        sim_config,
87        chunk,
88        seed_base,
89        overrides: IntentOverrides::default(),
90    };
91
92    simulate_intent_request(catalog, request, resolver, progress).await
93}
94
95/// Trace-mode entry point used by the in-browser preview pane.
96pub async fn simulate_intent_with_trace<T>(
97    catalog: &ContentCatalog,
98    request: SimRequest<'_>,
99    resolver: &DynDataResolver<'_>,
100    progress: &dyn ProgressSink,
101    trace_sink: T,
102) -> Result<ChunkReport, ApplicationError>
103where
104    T: wowlab_engine_ports::DecisionTraceSink + 'static,
105{
106    let (bootstrapped, effective_seed) = bootstrap_chunk(
107        catalog,
108        request.sim_config,
109        request.chunk,
110        request.seed_base,
111        resolver,
112        &request.overrides,
113    )
114    .await?;
115
116    run_chunk_with_trace(
117        crate::ChunkRun {
118            assignment: request.chunk,
119            duration_s: bootstrapped.duration_s,
120            seed_base: effective_seed,
121        },
122        progress,
123        trace_sink,
124        handler_factory(bootstrapped.handler),
125    )
126}