core: More goto fixes

This commit is contained in:
Mike Welsh 2019-09-17 15:48:22 -07:00
parent ae80900f17
commit a331c565b3
12 changed files with 96 additions and 51 deletions

View File

@ -1195,7 +1195,8 @@ impl<'gc> Avm1<'gc> {
} }
fn action_stop_sounds(&mut self, _context: &mut ActionContext) -> Result<(), Error> { fn action_stop_sounds(&mut self, _context: &mut ActionContext) -> Result<(), Error> {
Err("Unimplemented action: StopSounds".into()) log::error!("Unimplemented action: StopSounds");
Ok(())
} }
fn action_store_register( fn action_store_register(

View File

@ -354,8 +354,12 @@ impl<'gc> MovieClip<'gc> {
TagCode::PlaceObject4 => { TagCode::PlaceObject4 => {
self.goto_place_object(reader, tag_len, 4, &mut goto_commands) self.goto_place_object(reader, tag_len, 4, &mut goto_commands)
} }
TagCode::RemoveObject => self.goto_remove_object(reader, 1, &mut goto_commands), TagCode::RemoveObject => {
TagCode::RemoveObject2 => self.goto_remove_object(reader, 2, &mut goto_commands), self.goto_remove_object(reader, 1, &mut goto_commands, is_rewind)
}
TagCode::RemoveObject2 => {
self.goto_remove_object(reader, 2, &mut goto_commands, is_rewind)
}
_ => Ok(()), _ => Ok(()),
}; };
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame); let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
@ -369,7 +373,7 @@ impl<'gc> MovieClip<'gc> {
// but BTreeMap::retain does not exist. // but BTreeMap::retain does not exist.
let mut children = std::mem::replace(&mut self.children, BTreeMap::new()); let mut children = std::mem::replace(&mut self.children, BTreeMap::new());
goto_commands.into_iter().for_each(|(depth, params)| { goto_commands.into_iter().for_each(|(depth, params)| {
let (needs_run, child) = match children.get_mut(&depth).copied() { let (was_instantiated, child) = match children.get_mut(&depth).copied() {
// For rewinds, if an object was created before the final frame, // For rewinds, if an object was created before the final frame,
// it will exist on the final frame as well. Re-use this object // it will exist on the final frame as well. Re-use this object
// instead of recreating. // instead of recreating.
@ -391,13 +395,15 @@ impl<'gc> MovieClip<'gc> {
// Apply final delta to display pamareters. // Apply final delta to display pamareters.
let child_node = child; let child_node = child;
let mut child = child.write(context.gc_context); let mut child = child.write(context.gc_context);
child.apply_place_object(params); child.apply_place_object(params.place_object);
if was_instantiated {
// We must run newly created objects for one frame // Set the placement frame for the new object to the frame
// to ensure they place any children objects. // it is actually created on.
// TODO: This will probably move as our order-of-execution child.set_place_frame(params.frame);
// becomes more accurate. // We must run newly created objects for one frame
if needs_run { // to ensure they place any children objects.
// TODO: This will probably move as our order-of-execution
// becomes more accurate.
context.active_clip = child_node; context.active_clip = child_node;
child.run_frame(context); child.run_frame(context);
context.active_clip = prev_active_clip; context.active_clip = prev_active_clip;
@ -423,13 +429,15 @@ impl<'gc> MovieClip<'gc> {
// Apply final delta to display pamareters. // Apply final delta to display pamareters.
let child_node = child; let child_node = child;
let mut child = child.write(context.gc_context); let mut child = child.write(context.gc_context);
child.apply_place_object(params); child.apply_place_object(params.place_object);
// We must run newly created objects for one frame
// to ensure they place any children objects.
// TODO: This will probably move as our order-of-execution
// becomes more accurate.
if id != 0 { if id != 0 {
// Set the placement frame for the new object to the frame
// it is actually created on.
child.set_place_frame(params.frame);
// We must run newly created objects for one frame
// to ensure they place any children objects.
// TODO: This will probably move as our order-of-execution
// becomes more accurate.
context.active_clip = child_node; context.active_clip = child_node;
child.run_frame(context); child.run_frame(context);
context.active_clip = prev_active_clip; context.active_clip = prev_active_clip;
@ -449,9 +457,9 @@ impl<'gc> MovieClip<'gc> {
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
tag_len: usize, tag_len: usize,
version: u8, version: u8,
goto_commands: &mut fnv::FnvHashMap<Depth, swf::PlaceObject>, goto_commands: &mut fnv::FnvHashMap<Depth, GotoPlaceObject>,
) -> DecodeResult { ) -> DecodeResult {
let mut place_object = if version == 1 { let place_object = if version == 1 {
reader.read_place_object(tag_len) reader.read_place_object(tag_len)
} else { } else {
reader.read_place_object_2_or_3(version) reader.read_place_object_2_or_3(version)
@ -459,10 +467,14 @@ impl<'gc> MovieClip<'gc> {
// We merge the deltas from this PlaceObject with the previous command. // We merge the deltas from this PlaceObject with the previous command.
let depth = place_object.depth; let depth = place_object.depth;
let mut goto_place = GotoPlaceObject {
frame: self.current_frame,
place_object,
};
goto_commands goto_commands
.entry(depth) .entry(depth)
.and_modify(|prev_place| prev_place.merge(&mut place_object)) .and_modify(|prev_place| prev_place.merge(&mut goto_place))
.or_insert(place_object); .or_insert(goto_place);
Ok(()) Ok(())
} }
@ -473,17 +485,23 @@ impl<'gc> MovieClip<'gc> {
&mut self, &mut self,
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
version: u8, version: u8,
goto_commands: &mut fnv::FnvHashMap<Depth, swf::PlaceObject>, goto_commands: &mut fnv::FnvHashMap<Depth, GotoPlaceObject>,
is_rewind: bool,
) -> DecodeResult { ) -> DecodeResult {
let remove_object = if version == 1 { let remove_object = if version == 1 {
reader.read_remove_object_1() reader.read_remove_object_1()
} else { } else {
reader.read_remove_object_2() reader.read_remove_object_2()
}?; }?;
// If this tag were to remove an object that existed before the goto,
// then we can remove that child right away.
goto_commands.remove(&remove_object.depth); goto_commands.remove(&remove_object.depth);
self.children.remove(&remove_object.depth); if !is_rewind {
// For fast-forwards, if this tag were to remove an object
// that existed before the goto, then we can remove that child right away.
// Don't do this for rewinds, because they conceptually
// start from an empty display list, and we also want to examine
// the old children to decide if they persist (place_frame <= goto_frame).
self.children.remove(&remove_object.depth);
}
Ok(()) Ok(())
} }
} }
@ -1151,7 +1169,11 @@ impl<'gc, 'a> MovieClip<'gc> {
context, context,
id, id,
place_object.depth, place_object.depth,
place_object.modifies_original_item(), if let PlaceObjectAction::Replace(_) = place_object.action {
true
} else {
false
},
) { ) {
child child
.write(context.gc_context) .write(context.gc_context)
@ -1270,16 +1292,20 @@ unsafe impl<'gc> gc_arena::Collect for MovieClipStatic {
} }
} }
pub trait PlaceObjectExt { /// Stores the placement settings for display objects during a
fn id(&self) -> CharacterId; /// goto command.
fn modifies_original_item(&self) -> bool; #[derive(Debug)]
fn merge(&mut self, next: &mut swf::PlaceObject); struct GotoPlaceObject {
/// The frame number that this character was first placed on.
frame: FrameNumber,
/// The display properties of the object.
place_object: swf::PlaceObject,
} }
impl PlaceObjectExt for swf::PlaceObject { impl GotoPlaceObject {
#[inline] #[inline]
fn id(&self) -> CharacterId { fn id(&self) -> CharacterId {
match &self.action { match &self.place_object.action {
swf::PlaceObjectAction::Place(id) | swf::PlaceObjectAction::Replace(id) => *id, swf::PlaceObjectAction::Place(id) | swf::PlaceObjectAction::Replace(id) => *id,
swf::PlaceObjectAction::Modify => 0, swf::PlaceObjectAction::Modify => 0,
} }
@ -1287,39 +1313,46 @@ impl PlaceObjectExt for swf::PlaceObject {
#[inline] #[inline]
fn modifies_original_item(&self) -> bool { fn modifies_original_item(&self) -> bool {
if let swf::PlaceObjectAction::Replace(_) = &self.action { if let swf::PlaceObjectAction::Replace(_) = &self.place_object.action {
true true
} else { } else {
false false
} }
} }
fn merge(&mut self, next: &mut swf::PlaceObject) { fn merge(&mut self, next: &mut GotoPlaceObject) {
use swf::PlaceObjectAction; use swf::PlaceObjectAction;
self.action = match (self.action, next.action) { let cur_place = &mut self.place_object;
(prev, PlaceObjectAction::Modify) => prev, let next_place = &mut next.place_object;
(_, next) => next, match (cur_place.action, next_place.action) {
(cur, PlaceObjectAction::Modify) => {
cur_place.action = cur;
}
(_, new) => {
cur_place.action = new;
self.frame = next.frame;
}
}; };
if next.matrix.is_some() { if next_place.matrix.is_some() {
self.matrix = next.matrix.take(); cur_place.matrix = next_place.matrix.take();
} }
if next.color_transform.is_some() { if next_place.color_transform.is_some() {
self.color_transform = next.color_transform.take(); cur_place.color_transform = next_place.color_transform.take();
} }
if next.ratio.is_some() { if next_place.ratio.is_some() {
self.ratio = next.ratio.take(); cur_place.ratio = next_place.ratio.take();
} }
if next.name.is_some() { if next_place.name.is_some() {
self.name = next.name.take(); cur_place.name = next_place.name.take();
} }
if next.clip_depth.is_some() { if next_place.clip_depth.is_some() {
self.clip_depth = next.clip_depth.take(); cur_place.clip_depth = next_place.clip_depth.take();
} }
if next.class_name.is_some() { if next_place.class_name.is_some() {
self.class_name = next.class_name.take(); cur_place.class_name = next_place.class_name.take();
} }
if next.background_color.is_some() { if next_place.background_color.is_some() {
self.background_color = next.background_color.take(); cur_place.background_color = next_place.background_color.take();
} }
// TODO: Other stuff. // TODO: Other stuff.
} }

View File

@ -36,8 +36,11 @@ swf_tests! {
(looping, "avm1/looping", 6), (looping, "avm1/looping", 6),
(goto_advance1, "avm1/goto_advance1", 10), (goto_advance1, "avm1/goto_advance1", 10),
(goto_advance2, "avm1/goto_advance2", 10), (goto_advance2, "avm1/goto_advance2", 10),
(goto_both_ways1, "avm1/goto_both_ways1", 10),
(goto_both_ways2, "avm1/goto_both_ways2", 10),
(goto_rewind1, "avm1/goto_rewind1", 10), (goto_rewind1, "avm1/goto_rewind1", 10),
(goto_rewind2, "avm1/goto_rewind2", 10), (goto_rewind2, "avm1/goto_rewind2", 10),
(goto_rewind3, "avm1/goto_rewind3", 10),
(tell_target, "avm1/tell_target", 3), (tell_target, "avm1/tell_target", 3),
} }

View File

@ -0,0 +1,3 @@
gotoAndPlay(3)
child frame 1
gotoAndStop(2)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
child frame 1
gotoAndPlay(5)
gotoAndStop(3)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@
gotoAndPlay(4)
child frame 1

Binary file not shown.

Binary file not shown.