core: In AVM2, run removals before frame advance.

We still retain the queue system as events are fired at removal time, and those events can trigger more gotos. If such a goto happens, AS3 code will hit a clip still in the old state rather than an inconsistent one. I don't have test coverage for this exact scenario just yet.
This commit is contained in:
David Wendt 2022-08-02 21:36:02 -04:00 committed by kmeisthax
parent d691543c4c
commit 7a09bbfaab
1 changed files with 25 additions and 18 deletions

View File

@ -1111,7 +1111,10 @@ impl<'gc> MovieClip<'gc> {
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
run_display_actions: bool, run_display_actions: bool,
) { ) {
match self.determine_next_frame() { let next_frame = self.determine_next_frame();
match next_frame {
//Removals happen before frame advance.
NextFrame::Next if context.is_action_script_3() => {}
NextFrame::Next => self.0.write(context.gc_context).current_frame += 1, NextFrame::Next => self.0.write(context.gc_context).current_frame += 1,
NextFrame::First => return self.run_goto(context, 1, true), NextFrame::First => return self.run_goto(context, 1, true),
NextFrame::Same => self.stop(context), NextFrame::Same => self.stop(context),
@ -1169,7 +1172,24 @@ impl<'gc> MovieClip<'gc> {
}; };
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame); let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
let remove_actions = self.unqueue_tags_matching(context, |t| {
matches!(t.tag_type, QueuedTagAction::Remove(_))
});
for (_, tag) in remove_actions {
let mut reader = data.read_from(tag.tag_start);
let version = match tag.tag_type {
QueuedTagAction::Remove(v) => v,
_ => unreachable!(),
};
if let Err(e) = self.remove_object(context, &mut reader, version) {
log::error!("Error running queued tag: {:?}, got {}", tag.tag_type, e);
}
}
let mut write = self.0.write(context.gc_context); let mut write = self.0.write(context.gc_context);
write.tag_stream_pos = reader.get_ref().as_ptr() as u64 - tag_stream_start; write.tag_stream_pos = reader.get_ref().as_ptr() as u64 - tag_stream_start;
// Check if our audio track has finished playing. // Check if our audio track has finished playing.
@ -1179,6 +1199,10 @@ impl<'gc> MovieClip<'gc> {
} }
} }
if matches!(next_frame, NextFrame::Next) && context.is_action_script_3() {
write.current_frame += 1;
}
let frame_id = write.current_frame; let frame_id = write.current_frame;
write.queued_script_frame = Some(frame_id); write.queued_script_frame = Some(frame_id);
} }
@ -1962,23 +1986,6 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
if is_playing { if is_playing {
self.run_frame_internal(context, true); self.run_frame_internal(context, true);
let data = self.0.read().static_data.swf.clone();
let remove_actions = self.unqueue_tags_matching(context, |t| {
matches!(t.tag_type, QueuedTagAction::Remove(_))
});
for (_, tag) in remove_actions {
let mut reader = data.read_from(tag.tag_start);
let version = match tag.tag_type {
QueuedTagAction::Remove(v) => v,
_ => unreachable!(),
};
if let Err(e) = self.remove_object(context, &mut reader, version) {
log::error!("Error running queued tag: {:?}, got {}", tag.tag_type, e);
}
}
} }
} }
} }