From f3ae65df9366e993f306318b049a8dd7e2552c88 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Fri, 23 Oct 2020 22:19:30 -0400 Subject: [PATCH] core: Add flag for display objects which were placed by AS3. In AS3. objects placed by scripts *cannot* be removed by `RemoveObject` tags. This enforces that in Ruffle. --- core/src/display_object.rs | 32 +++++++++++++++++++++++++++ core/src/display_object/movie_clip.rs | 32 ++++++++++++++++----------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 4cae381aa..fdbed53c4 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -358,6 +358,18 @@ impl<'gc> DisplayObjectBase<'gc> { } } + fn placed_by_script(&self) -> bool { + self.flags.contains(DisplayObjectFlags::PlacedByScript) + } + + fn set_placed_by_script(&mut self, value: bool) { + if value { + self.flags.insert(DisplayObjectFlags::PlacedByScript); + } else { + self.flags.remove(DisplayObjectFlags::PlacedByScript); + } + } + fn swf_version(&self) -> u8 { self.parent .map(|p| p.swf_version()) @@ -736,6 +748,16 @@ pub trait TDisplayObject<'gc>: false } + /// Whether this display object has been created by ActionScript 3. + /// When this flag is set, changes from SWF `RemoveObject` tags are + /// ignored. + fn placed_by_script(&self) -> bool; + + /// Sets whether this display object has been created by ActionScript 3. + /// When this flag is set, changes from SWF `RemoveObject` tags are + /// ignored. + fn set_placed_by_script(&self, context: MutationContext<'gc, '_>, value: bool); + /// Executes and propagates the given clip event. /// Events execute inside-out; the deepest child will react first, followed by its parent, and /// so forth. @@ -1123,6 +1145,12 @@ macro_rules! impl_display_object_sansbounds { .$field .set_transformed_by_script(value) } + fn placed_by_script(&self) -> bool { + self.0.read().$field.placed_by_script() + } + fn set_placed_by_script(&self, context: gc_arena::MutationContext<'gc, '_>, value: bool) { + self.0.write(context).$field.set_placed_by_script(value) + } fn instantiate( &self, gc_context: gc_arena::MutationContext<'gc, '_>, @@ -1255,6 +1283,10 @@ enum DisplayObjectFlags { /// Whether this object has been transformed by ActionScript. /// When this flag is set, changes from SWF `PlaceObject` tags are ignored. TransformedByScript, + + /// Whether this object has been placed on the timeline by ActionScript 3. + /// When this flag is set, changes from SWF `RemoveObject` tags are ignored. + PlacedByScript, } pub struct ChildIter<'gc> { diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 08496d593..b6fabb94f 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -1198,7 +1198,9 @@ impl<'gc> MovieClip<'gc> { .children .insert(depth, child); if let Some(prev_child) = prev_child { - self.remove_child_from_exec_list(context, prev_child); + if !prev_child.placed_by_script() { + self.remove_child_from_exec_list(context, prev_child); + } } self.0 .write(context.gc_context) @@ -1275,8 +1277,10 @@ impl<'gc> MovieClip<'gc> { }) .collect(); for (depth, child) in children { - self.0.write(context.gc_context).children.remove(&depth); - self.remove_child_from_exec_list(context, child); + if !child.placed_by_script() { + self.0.write(context.gc_context).children.remove(&depth); + self.remove_child_from_exec_list(context, child); + } } true } else { @@ -1704,9 +1708,11 @@ impl<'gc> MovieClip<'gc> { // 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). - let child = self.0.write(context.gc_context).children.remove(&depth); - if let Some(child) = child { - self.remove_child_from_exec_list(context, child); + if let Some(child) = self.0.read().children.get(&depth).cloned() { + if !child.placed_by_script() { + self.0.write(context.gc_context).children.remove(&depth); + self.remove_child_from_exec_list(context, child); + } } } Ok(()) @@ -2944,14 +2950,14 @@ impl<'gc, 'a> MovieClip<'gc> { } else { reader.read_remove_object_2() }?; - let child = self - .0 - .write(context.gc_context) - .children - .remove(&remove_object.depth.into()); - if let Some(child) = child { - self.remove_child_from_exec_list(context, child); + + if let Some(child) = self.0.read().children.get(&remove_object.depth.into()).cloned() { + if !child.placed_by_script() { + self.0.write(context.gc_context).children.remove(&remove_object.depth.into()); + self.remove_child_from_exec_list(context, child); + } } + Ok(()) }