Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVM2 Queued Tags & Looping Gotos fixes #7570

Merged
merged 47 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fe8dbf6
tests: Add a test for the order of events surrounding `gotoAndStop`
kmeisthax Dec 16, 2021
65fd3ae
tests: Log `this.currentFrame` on every event watcher we can.
kmeisthax Dec 16, 2021
d8114a1
tests: `as3_movieclip_displayevents_timeline` now explicitly winds a …
kmeisthax Dec 29, 2021
78700e7
tests: Fix the test output for `as3_movieclip_displayevents_dblhandler`.
kmeisthax Jan 4, 2022
d524f99
tests: Add tests for playing and pausing a movie clip in `enterFrame`…
kmeisthax Jan 24, 2022
4111ebd
tests: Oops, forgot to recompile the exitframe play test.
kmeisthax Jan 24, 2022
4d61643
tests: `as3_movieclip_displayevents_xyzframeplay` tests should also l…
kmeisthax Jan 25, 2022
cf31344
tests: `as3_movieclip_displayevents_looping` should have a `RemoveObj…
kmeisthax Jan 25, 2022
b70f25a
tests: `as3_movieclip_displayevents_timeline` should log events on th…
kmeisthax Jan 25, 2022
a7d308d
tests: Add tests for AS3 gotos during `enterFrame`, `frameConstructed…
kmeisthax Jan 26, 2022
0e04ab1
tests: Extend the goto tests with more non-looping jumps
kmeisthax Feb 1, 2022
9c42a0e
tests: Add tests for symbol instantiation in `enterFrame`, `exitFrame…
kmeisthax Feb 3, 2022
cf6b599
tests: Add timeline and stage manipulation tests at user event time.
kmeisthax Apr 30, 2022
1e4aec8
tests: Add a second, more thorough `clickgoto2` test
kmeisthax May 1, 2022
a8bdda4
tests: Ignore the `as3_simplebutton_constr_childevents` test as it is…
kmeisthax May 18, 2022
dfca46c
tests: Add a goto-and-stop test that inspects the children after the …
kmeisthax Jan 8, 2022
118b272
tests: Add tests for gotos interacting with frame scripts.
kmeisthax Jan 8, 2022
4d3d234
core: `enter_frame` should be an event that all display objects get a…
kmeisthax Dec 17, 2021
9e527f7
core: In AVM2, movie clips process `RemoveObject` before any other fr…
kmeisthax Dec 16, 2021
a14787a
core: Frame actions should start with `enterFrame`; not `exitFrame`.
kmeisthax Dec 17, 2021
a18581d
core: Track what part of the frame processing loop we're in
kmeisthax Dec 28, 2021
bb1f76a
core: Add a new frame phase to represent non-frame work such as input…
kmeisthax Jan 11, 2022
e282984
core: `Player` should start in the `Idle` phase
kmeisthax Jan 22, 2022
2f56670
core: Centralize all the various calls to `enter_frame`/`construct_fr…
kmeisthax Dec 28, 2021
efa1aaa
core: Movie clips run all their tags at `enter_frame`, and place/remo…
kmeisthax Jul 28, 2022
93525df
core: Remove AVM2 compatibility hacks surrounding place frame.
kmeisthax Jul 28, 2022
b7b0bb0
core: `frameConstructed` during a goto fires after the goto commands …
kmeisthax Dec 15, 2021
c8a1fe5
core: Explicit gotos must run even if they are no-ops.
kmeisthax Jan 8, 2022
c4078cc
core: Same-frame gotos should only proceed on AVM2.
kmeisthax Jan 22, 2022
bff72f4
core: Movie clip rewinds should not remove any children until after t…
kmeisthax Dec 29, 2021
af3aed2
core: Queue new object placement at loop time on the tag queue.
kmeisthax Jul 29, 2022
cb2704e
core: Fast-forward removals as the result of a goto happen before the…
kmeisthax Feb 2, 2022
9d58aec
core: Child removals appear to resolve in reverse render order.
kmeisthax May 1, 2022
e88dc81
core: In AVM2, run removals before frame advance.
kmeisthax Aug 3, 2022
d7f3b5c
core: No-op gotos should be treated as rewinding.
kmeisthax Aug 3, 2022
7adca76
core: Allow queueing both a remove and a place tag at the same depth,…
kmeisthax Aug 3, 2022
e82c716
core: Gotos *always* queue the target frame's scripts on the target c…
kmeisthax Jan 30, 2022
fb54f12
core: Only force-queue frame scripts if the goto is not a no-op.
kmeisthax Feb 2, 2022
eba1134
core: Only `Construct` and `Enter` phases want forced script frames o…
kmeisthax Aug 3, 2022
3475e58
core: Fast-forwarding gotos should run frame scripts on all removed c…
kmeisthax Feb 3, 2022
01102b6
core: Clear queued tags if AS3 attempts a goto in the middle of a loop.
kmeisthax Aug 4, 2022
8568cfb
core: Explicit gotos drain the tag queue of any prior operations.
kmeisthax Aug 4, 2022
ab11022
tests: Fix regression in `as3_loaderinfo_events` caused by the frame …
kmeisthax Aug 4, 2022
71807f7
core: Fix a potential stack overflow caused by gotos triggering frame…
kmeisthax Aug 4, 2022
68ddaf2
core: Replace `Vec` of queued tags with a more restrictive enum that …
kmeisthax Aug 13, 2022
1a712f6
docs: Add comments documenting subtle points of AS3 loop & tag queuei…
kmeisthax Aug 24, 2022
9011e7a
core: Use catchup_display_object_to_frame in Loader
Aaron1011 Aug 24, 2022
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
3 changes: 2 additions & 1 deletion core/src/avm2/globals/flash/display/displayobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::avm2::Error;
use crate::avm2::Namespace;
use crate::avm2::QName;
use crate::display_object::{HitTestOptions, TDisplayObject};
use crate::frame_lifecycle::catchup_display_object_to_frame;
use crate::string::AvmString;
use crate::types::{Degrees, Percent};
use crate::vminterface::Instantiator;
Expand Down Expand Up @@ -57,7 +58,7 @@ pub fn native_instance_init<'gc>(
child.set_object2(activation.context.gc_context, this);

child.post_instantiation(&mut activation.context, None, Instantiator::Avm2, false);
child.construct_frame(&mut activation.context);
catchup_display_object_to_frame(&mut activation.context, child);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::context_menu::ContextMenuState;
use crate::display_object::{EditText, InteractiveObject, MovieClip, SoundTransform, Stage};
use crate::external::ExternalInterface;
use crate::focus_tracker::FocusTracker;
use crate::frame_lifecycle::FramePhase;
use crate::library::Library;
use crate::loader::LoadManager;
use crate::player::Player;
Expand Down Expand Up @@ -168,6 +169,11 @@ pub struct UpdateContext<'a, 'gc, 'gc_context> {

/// Amount of actions performed since the last timeout check
pub actions_since_timeout_check: &'a mut u16,

/// The current frame processing phase.
///
/// If we are not doing frame processing, then this is `FramePhase::Enter`.
pub frame_phase: &'a mut FramePhase,
}

/// Convenience methods for controlling audio.
Expand Down Expand Up @@ -327,6 +333,7 @@ impl<'a, 'gc, 'gc_context> UpdateContext<'a, 'gc, 'gc_context> {
time_offset: self.time_offset,
frame_rate: self.frame_rate,
actions_since_timeout_check: self.actions_since_timeout_check,
frame_phase: self.frame_phase,
}
}

Expand Down
18 changes: 4 additions & 14 deletions core/src/display_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,20 +1094,10 @@ pub trait TDisplayObject<'gc>:
.set_instantiated_by_timeline(value);
}

/// Emit an `enterFrame` event on this DisplayObject and any children it
/// may have.
fn enter_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
let enter_frame_evt = Avm2EventObject::bare_default_event(context, "enterFrame");

let dobject_constr = context.avm2.classes().display_object;

if let Err(e) = Avm2::broadcast_event(context, enter_frame_evt, dobject_constr) {
log::error!(
"Encountered AVM2 error when broadcasting enterFrame event: {}",
e
);
}
}
/// Run any start-of-frame actions for this display object.
///
/// When fired on `Stage`, this also emits the AVM2 `enterFrame` broadcast.
fn enter_frame(&self, _context: &mut UpdateContext<'_, 'gc, '_>) {}

/// Construct all display objects that the timeline indicates should exist
/// this frame, and their children.
Expand Down
29 changes: 26 additions & 3 deletions core/src/display_object/avm2_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::display_object::interactive::{
};
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, MovieClip, TDisplayObject};
use crate::events::{ClipEvent, ClipEventResult};
use crate::frame_lifecycle::catchup_display_object_to_frame;
use crate::prelude::*;
use crate::tag_utils::{SwfMovie, SwfSlice};
use crate::vminterface::Instantiator;
Expand Down Expand Up @@ -224,15 +225,15 @@ impl<'gc> Avm2Button<'gc> {

child.set_parent(context.gc_context, Some(self.into()));
child.post_instantiation(context, None, Instantiator::Movie, false);
child.construct_frame(context);
catchup_display_object_to_frame(context, child);

(child, false)
} else {
let state_sprite = MovieClip::new(movie, context.gc_context);

state_sprite.set_avm2_class(context.gc_context, Some(sprite_class));
state_sprite.set_parent(context.gc_context, Some(self.into()));
state_sprite.construct_frame(context);
catchup_display_object_to_frame(context, state_sprite.into());

for (child, depth) in children {
// `parent` returns `null` for these grandchildren during construction time, even though
Expand All @@ -242,7 +243,7 @@ impl<'gc> Avm2Button<'gc> {
state_sprite.replace_at_depth(context, child, depth.into());
child.set_parent(context.gc_context, Some(self.into()));
child.post_instantiation(context, None, Instantiator::Movie, false);
child.construct_frame(context);
catchup_display_object_to_frame(context, child);
child.set_parent(context.gc_context, Some(state_sprite.into()));
}

Expand Down Expand Up @@ -428,6 +429,28 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
self.set_state(context, ButtonState::Up);
}

fn enter_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
let hit_area = self.0.read().hit_area;
if let Some(hit_area) = hit_area {
hit_area.enter_frame(context);
}

let up_state = self.0.read().up_state;
if let Some(up_state) = up_state {
up_state.enter_frame(context);
}

let down_state = self.0.read().down_state;
if let Some(down_state) = down_state {
down_state.enter_frame(context);
}

let over_state = self.0.read().over_state;
if let Some(over_state) = over_state {
over_state.enter_frame(context);
}
}

fn construct_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
let hit_area = self.0.read().hit_area;
if let Some(hit_area) = hit_area {
Expand Down
Loading