Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/engine/src/game/effects/change_zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ pub fn resolve(
per_obj_ctx.source_id,
);
let delivery_start = events.len();
let stack_depth_before_zone_move = state.resolution_stack.len();
let stack_depth_before_zone_move = state.resolution_stack.capture_child_boundary();
match process_one_zone_move_with_terminal(state, &per_obj_ctx, *obj_id, events) {
crate::game::zone_pipeline::ZoneMoveTerminalResult::Completed(completion) => {
logical_zone_change_group
Expand Down Expand Up @@ -1996,7 +1996,7 @@ pub fn resolve_all(
let anticipated_pause =
anticipated_zone_change_delivery(state, obj_id, dest_zone, ability.source_id);
let delivery_start = events.len();
let stack_depth_before_zone_move = state.resolution_stack.len();
let stack_depth_before_zone_move = state.resolution_stack.capture_child_boundary();
match crate::game::zone_pipeline::execute_zone_move_with_terminal_and_controller(
state,
obj_id,
Expand Down
13 changes: 9 additions & 4 deletions crates/engine/src/game/effects/each_player_copy_chosen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::types::game_state::{
};
use crate::types::identifiers::ObjectId;
use crate::types::player::PlayerId;
use crate::types::resolution::ChildStackDepth;

/// Effect parameters threaded through the whole APNAP walk. Bundled so the
/// resolver, the `SelectTargets` continuation, and the replacement-resume drain
Expand Down Expand Up @@ -263,7 +264,7 @@ pub(crate) fn drive_from_copy(
);
// Depth 1: skip the depth-0 chain prelude so per-resolution ledgers
// (`last_created_token_ids`) are not reset.
let stack_depth_before_copy = state.resolution_stack.len();
let stack_depth_before_copy = state.resolution_stack.capture_child_boundary();
super::resolve_ability_chain(state, &copy_ability, events, 1)?;

// CR 616.1: The copy parked a replacement-ordering choice. Do NOT read
Expand Down Expand Up @@ -326,7 +327,7 @@ pub(crate) fn perform_counter_step_then_advance(
params.source_id,
player,
);
let stack_depth_before_counter = state.resolution_stack.len();
let stack_depth_before_counter = state.resolution_stack.capture_child_boundary();
super::resolve_ability_chain(state, &counter_ability, events, 1)?;
// CR 616.1: the counter placement paused for a replacement
// ordering — park an `AwaitingCounters` continuation.
Expand Down Expand Up @@ -398,9 +399,13 @@ pub(crate) fn drain_pending(state: &mut GameState, events: &mut Vec<GameEvent>)
fn park_each_player_copy_chosen_after_current_step(
state: &mut GameState,
pending: PendingEachPlayerCopyChosen,
stack_depth_before_step: usize,
stack_depth_before_step: ChildStackDepth,
) {
match state.resolution_stack.len().cmp(&stack_depth_before_step) {
match state
.resolution_stack
.capture_child_boundary()
.cmp(&stack_depth_before_step)
{
std::cmp::Ordering::Less => {
panic!("each-player-copy-chosen step removed a parent before it could be re-parked")
}
Expand Down
26 changes: 13 additions & 13 deletions crates/engine/src/game/effects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use crate::types::identifiers::{ObjectId, TrackedSetId};
use crate::types::mana::ManaCost;
use crate::types::player::{Player, PlayerId};
use crate::types::resolution::{
AbilityContinuationFrame, FrameGate, OptionalEffectFrame, PendingRepeatedOptionalPayment,
RepeatedOptionalPaymentFrame, ResolutionFrame,
AbilityContinuationFrame, ChildStackDepth, FrameGate, OptionalEffectFrame,
PendingRepeatedOptionalPayment, RepeatedOptionalPaymentFrame, ResolutionFrame,
};
use crate::types::zones::Zone;

Expand Down Expand Up @@ -1184,11 +1184,11 @@ fn drain_active_repeat_until(state: &mut GameState) {
fn park_repeat_until_after_inner_pause(
state: &mut GameState,
pending: crate::types::game_state::PendingRepeatUntil,
stack_depth_before_iteration: usize,
stack_depth_before_iteration: ChildStackDepth,
) {
match state
.resolution_stack
.len()
.capture_child_boundary()
.cmp(&stack_depth_before_iteration)
{
std::cmp::Ordering::Less => {
Expand Down Expand Up @@ -1362,7 +1362,7 @@ fn drain_pending_change_zone_iteration(state: &mut GameState, events: &mut Vec<G
)
});
let delivery_start = events.len();
let stack_depth_before_zone_move = state.resolution_stack.len();
let stack_depth_before_zone_move = state.resolution_stack.capture_child_boundary();
match crate::game::effects::change_zone::process_one_zone_move_with_terminal(
state, &ctx, *obj_id, events,
) {
Expand Down Expand Up @@ -1618,7 +1618,7 @@ fn drain_active_repeat_for(state: &mut GameState, events: &mut Vec<GameEvent>) {
} else {
&ability
};
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
// CR 608.2c + CR 109.5: Drive the FULL chain (parent effect +
// sub_ability + line-1660 continuation wiring) for each resumed
// iteration, mirroring iteration 0's path. Calling `resolve_effect`
Expand Down Expand Up @@ -1680,11 +1680,11 @@ fn drain_active_repeat_for(state: &mut GameState, events: &mut Vec<GameEvent>) {
fn park_repeat_for_after_current_iteration(
state: &mut GameState,
pending: crate::types::game_state::PendingRepeatIteration,
stack_depth_before_iteration: usize,
stack_depth_before_iteration: ChildStackDepth,
) {
match state
.resolution_stack
.len()
.capture_child_boundary()
.cmp(&stack_depth_before_iteration)
{
std::cmp::Ordering::Less => {
Expand Down Expand Up @@ -7422,7 +7422,7 @@ fn drive_repeat_for_outermost(
while iteration < base_iterations {
let mut iter_ability = effective.clone();
iter_ability.repeat_for = None;
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
resolve_chain_body(state, &iter_ability, events, depth)?;
if state.waiting_for != initial_waiting_for
|| (!initial_continuation_present && state.active_ability_continuation().is_some())
Expand Down Expand Up @@ -9520,7 +9520,7 @@ pub fn resolve_ability_chain(
None => resolve_chain_body(state, ability, events, depth),
Some(RepeatContinuation::ControllerChoice) => {
let initial_waiting_for = state.waiting_for.clone();
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
resolve_chain_body(state, ability, events, depth)?;
if state.waiting_for != initial_waiting_for {
// Inner pause: stash so the drain re-sets the repeat prompt
Expand All @@ -9547,7 +9547,7 @@ pub fn resolve_ability_chain(
stop_on_duplicate_exiled_names,
}) => loop {
let initial_waiting_for = state.waiting_for.clone();
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
resolve_chain_body(state, ability, events, depth)?;
if state.waiting_for != initial_waiting_for {
park_repeat_until_after_inner_pause(
Expand Down Expand Up @@ -9597,7 +9597,7 @@ pub fn resolve_ability_chain(
// iteration's stale result.
state.resolution_coin_flip = None;
let initial_waiting_for = state.waiting_for.clone();
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
resolve_chain_body(state, ability, events, depth)?;
if state.waiting_for != initial_waiting_for {
// Inner pause: stash the loop ability with its remaining cap
Expand Down Expand Up @@ -11193,7 +11193,7 @@ fn resolve_chain_body(
} else {
effective
};
let stack_depth_before_iteration = state.resolution_stack.len();
let stack_depth_before_iteration = state.resolution_stack.capture_child_boundary();
// CR 608.2d: A kind-driven or member-driven iteration whose action
// is optional fires its per-iteration "you may" gate through the
// full chain. All other iterations resolve the effect directly —
Expand Down
11 changes: 8 additions & 3 deletions crates/engine/src/game/effects/token_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::types::identifiers::{CardId, ObjectId, ObjectIncarnationRef};
use crate::types::proposed_event::{
CopyTokenSpec, EtbTapState, ProposedEvent, TokenCharacteristics,
};
use crate::types::resolution::ChildStackDepth;
use crate::types::resolved_commands::{
ResolvedCopyBodyModifications, ResolvedTokenBody, ResolvedTokenCreationCommand,
};
Expand Down Expand Up @@ -285,7 +286,7 @@ fn drain_copy_token_resolution(
if batch.count == 0 {
continue;
}
let stack_depth_before_batch = state.resolution_stack.len();
let stack_depth_before_batch = state.resolution_stack.capture_child_boundary();
let spec = super::token::copy_probe_spec_for(
batch.copy.source_id,
batch.copy.controller,
Expand Down Expand Up @@ -357,9 +358,13 @@ fn drain_copy_token_resolution(
fn park_copy_token_after_current_batch(
state: &mut GameState,
pending: PendingCopyTokenResolution,
stack_depth_before_batch: usize,
stack_depth_before_batch: ChildStackDepth,
) {
match state.resolution_stack.len().cmp(&stack_depth_before_batch) {
match state
.resolution_stack
.capture_child_boundary()
.cmp(&stack_depth_before_batch)
{
std::cmp::Ordering::Less => {
panic!("copy-token batch removed a parent frame before it could be re-parked")
}
Expand Down
13 changes: 9 additions & 4 deletions crates/engine/src/game/effects/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::types::game_state::{
};
use crate::types::identifiers::ObjectId;
use crate::types::player::PlayerId;
use crate::types::resolution::ChildStackDepth;

use super::resolve_ability_chain;
use crate::game::ability_utils::build_resolved_from_def;
Expand Down Expand Up @@ -381,7 +382,7 @@ pub fn resolve_tally(
// If a ballot parks an interactive choice (e.g. ChooseFromZoneChoice),
// stash remaining voters and return early; the drain function resumes.
let initial_waiting_for = state.waiting_for.clone();
let stack_depth_before_ballot = state.resolution_stack.len();
let stack_depth_before_ballot = state.resolution_stack.capture_child_boundary();
let mut remaining_voters: Vec<PlayerId> = choice_ballots.clone();

while let Some(voter) = remaining_voters.first().copied() {
Expand Down Expand Up @@ -654,7 +655,7 @@ pub(crate) fn drain_active_vote_ballot(state: &mut GameState, events: &mut Vec<G
let source_id = pending.source_id;
let controller = pending.controller;
let template = pending.ability_template;
let stack_depth_before_ballot = state.resolution_stack.len();
let stack_depth_before_ballot = state.resolution_stack.capture_child_boundary();

while let Some(voter) = remaining_voters.first().copied() {
remaining_voters.remove(0);
Expand Down Expand Up @@ -694,9 +695,13 @@ pub(crate) fn drain_active_vote_ballot(state: &mut GameState, events: &mut Vec<G
fn park_vote_ballot_after_current_ballot(
state: &mut GameState,
pending: PendingVoteBallotIteration,
stack_depth_before_ballot: usize,
stack_depth_before_ballot: ChildStackDepth,
) {
match state.resolution_stack.len().cmp(&stack_depth_before_ballot) {
match state
.resolution_stack
.capture_child_boundary()
.cmp(&stack_depth_before_ballot)
{
std::cmp::Ordering::Less => {
panic!("vote ballot removed a parent frame before it could be re-parked")
}
Expand Down
6 changes: 3 additions & 3 deletions crates/engine/src/game/engine_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ fn drain_debug_card_entries(
events: &mut Vec<GameEvent>,
) {
while pending.remaining > 0 && matches!(state.waiting_for, WaitingFor::Priority { .. }) {
let child_stack_start = state.resolution_stack.len();
let child_stack_start = state.resolution_stack.capture_child_boundary();
let object_id = materialize_debug_card(
state,
&pending.source,
Expand All @@ -986,9 +986,9 @@ fn drain_debug_card_entries(
state.waiting_for = entry.waiting_for;

if !matches!(state.waiting_for, WaitingFor::Priority { .. })
|| state.resolution_stack.len() > child_stack_start
|| state.resolution_stack.capture_child_boundary() > child_stack_start
{
if state.resolution_stack.len() > child_stack_start {
if state.resolution_stack.capture_child_boundary() > child_stack_start {
state
.insert_debug_card_entries_parent_at_child_boundary(pending, child_stack_start)
.expect("debug-card parent must sit below the entry child stack");
Expand Down
35 changes: 22 additions & 13 deletions crates/engine/src/types/game_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ use super::replacements::ReplacementEvent;
#[cfg(debug_assertions)]
use super::resolution::debug_assert_runtime_resolution_invariants;
use super::resolution::{
AbilityContinuationFrame, ChangeZoneFrame, FrameGate, MultiDrawFrame, OptionalEffectFrame,
PendingCoinFlip, PendingMutateMerge, PendingProliferateActions, RepeatedOptionalPaymentFrame,
ResolutionFrame, ResolutionStack, ResolutionStackError, ResolutionStateWire,
AbilityContinuationFrame, ChangeZoneFrame, ChildStackDepth, FrameGate, MultiDrawFrame,
OptionalEffectFrame, PendingCoinFlip, PendingMutateMerge, PendingProliferateActions,
RepeatedOptionalPaymentFrame, ResolutionFrame, ResolutionStack, ResolutionStackError,
ResolutionStateWire,
};
use super::resolved_commands::{
ManaPaymentRecipient, ResolvedContinuousEffectCommand,
Expand Down Expand Up @@ -18826,9 +18827,13 @@ impl GameState {
pub fn push_change_zone_iteration_after_child(
&mut self,
pending: PendingChangeZoneIteration,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) {
match self.resolution_stack.len().cmp(&child_stack_start) {
match self
.resolution_stack
.capture_child_boundary()
.cmp(&child_stack_start)
{
std::cmp::Ordering::Less => {
panic!("ChangeZone move removed a parent before it could be parked")
}
Expand Down Expand Up @@ -18860,9 +18865,13 @@ impl GameState {
pub fn replace_active_change_zone_iteration_after_child(
&mut self,
pending: PendingChangeZoneIteration,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) {
match self.resolution_stack.len().cmp(&child_stack_start) {
match self
.resolution_stack
.capture_child_boundary()
.cmp(&child_stack_start)
{
std::cmp::Ordering::Less => {
panic!("ChangeZone move removed its active owner before it could be re-parked")
}
Expand Down Expand Up @@ -19063,7 +19072,7 @@ impl GameState {
pub fn insert_copy_token_parent_at_child_boundary(
&mut self,
pending: PendingCopyTokenResolution,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack
.insert_copy_token_parent_at_child_boundary(pending, child_stack_start)
Expand Down Expand Up @@ -19093,7 +19102,7 @@ impl GameState {
pub fn insert_debug_card_entries_parent_at_child_boundary(
&mut self,
pending: PendingDebugCardEntries,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack
.insert_debug_card_entries_parent_at_child_boundary(pending, child_stack_start)
Expand Down Expand Up @@ -19141,7 +19150,7 @@ impl GameState {
pub fn insert_each_player_copy_chosen_parent_at_child_boundary(
&mut self,
pending: PendingEachPlayerCopyChosen,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack
.insert_each_player_copy_chosen_parent_at_child_boundary(pending, child_stack_start)
Expand Down Expand Up @@ -19184,7 +19193,7 @@ impl GameState {
pub fn insert_repeat_for_parent_at_child_boundary(
&mut self,
pending: PendingRepeatIteration,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack.insert_parent_at_child_boundary(
super::resolution::ResolutionFrame::RepeatFor(pending),
Expand Down Expand Up @@ -19227,7 +19236,7 @@ impl GameState {
pub fn insert_repeat_until_parent_at_child_boundary(
&mut self,
pending: PendingRepeatUntil,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack.insert_parent_at_child_boundary(
super::resolution::ResolutionFrame::RepeatUntil(pending),
Expand Down Expand Up @@ -19274,7 +19283,7 @@ impl GameState {
pub fn insert_vote_ballot_parent_at_child_boundary(
&mut self,
pending: PendingVoteBallotIteration,
child_stack_start: usize,
child_stack_start: ChildStackDepth,
) -> Result<(), ResolutionStackError> {
self.resolution_stack.insert_parent_at_child_boundary(
super::resolution::ResolutionFrame::VoteBallot(pending),
Expand Down
8 changes: 4 additions & 4 deletions crates/engine/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ pub use proposed_event::{AppliedReplacementKey, ProposedEvent, ReplacementId};
pub use replacements::ReplacementEvent;
pub use replay::{RecordedAction, ReplayHeader, ReplayLog, REPLAY_FORMAT_VERSION};
pub use resolution::{
AbilityContinuationFrame, ChangeZoneFrame, DirectChoiceGate, FrameGate, FrameKind,
MultiDrawFrame, OptionalEffectFrame, PerCategoryZoneChoiceFrame, RepeatedOptionalPaymentFrame,
ResolutionFrame, ResolutionStack, ResolutionStackError, ResolutionStateWire,
RESOLUTION_STATE_WIRE_VERSION,
AbilityContinuationFrame, ChangeZoneFrame, ChildStackDepth, DirectChoiceGate, FrameGate,
FrameKind, MultiDrawFrame, OptionalEffectFrame, PerCategoryZoneChoiceFrame,
RepeatedOptionalPaymentFrame, ResolutionFrame, ResolutionStack, ResolutionStackError,
ResolutionStateWire, RESOLUTION_STATE_WIRE_VERSION,
};
pub use resolved_commands::{
ManaPaymentRecipient, ProducedManaUnit, ResolvedCommandJournalEntry, ResolvedCommandOrdinal,
Expand Down
Loading
Loading