avm1: Fire initialize clip event

This event fires for new clips before any construct clip events.

Split the action queue up into separate priorities, giving
initialize the highest priority.
This commit is contained in:
Mike Welsh 2020-11-08 23:43:41 -08:00
parent 316bc4e920
commit 3905837679
3 changed files with 55 additions and 31 deletions

View File

@ -252,19 +252,21 @@ unsafe impl<'gc> Collect for QueuedActions<'gc> {
/// Action and gotos need to be queued up to execute at the end of the frame.
pub struct ActionQueue<'gc> {
change_prototype_queue: VecDeque<QueuedActions<'gc>>,
action_queue: VecDeque<QueuedActions<'gc>>,
/// Each priority is kept in a separate bucket.
action_queue: Vec<VecDeque<QueuedActions<'gc>>>,
}
impl<'gc> ActionQueue<'gc> {
const DEFAULT_CAPACITY: usize = 32;
const NUM_PRIORITIES: usize = 3;
/// Crates a new `ActionQueue` with an empty queue.
pub fn new() -> Self {
Self {
change_prototype_queue: VecDeque::with_capacity(Self::DEFAULT_CAPACITY),
action_queue: VecDeque::with_capacity(Self::DEFAULT_CAPACITY),
let mut action_queue = Vec::with_capacity(Self::NUM_PRIORITIES);
for _ in 0..Self::NUM_PRIORITIES {
action_queue.push(VecDeque::with_capacity(Self::DEFAULT_CAPACITY))
}
Self { action_queue }
}
/// Queues ActionScript to run for the given movie clip.
@ -276,30 +278,28 @@ impl<'gc> ActionQueue<'gc> {
action_type: ActionType<'gc>,
is_unload: bool,
) {
// Prototype change goes a higher priority queue.
if let ActionType::Construct { .. } = action_type {
self.change_prototype_queue.push_back(QueuedActions {
let priority = action_type.priority();
let action = QueuedActions {
clip,
action_type,
is_unload,
})
} else {
self.action_queue.push_back(QueuedActions {
clip,
action_type,
is_unload,
})
};
debug_assert!(priority < Self::NUM_PRIORITIES);
if let Some(queue) = self.action_queue.get_mut(priority) {
queue.push_back(action)
}
}
/// Sorts and drains the actions from the queue.
pub fn pop_action(&mut self) -> Option<QueuedActions<'gc>> {
if !self.change_prototype_queue.is_empty() {
self.change_prototype_queue.pop_front()
} else {
self.action_queue.pop_front()
for queue in self.action_queue.iter_mut().rev() {
let action = queue.pop_front();
if action.is_some() {
return action;
}
}
None
}
}
impl<'gc> Default for ActionQueue<'gc> {
@ -311,8 +311,9 @@ impl<'gc> Default for ActionQueue<'gc> {
unsafe impl<'gc> Collect for ActionQueue<'gc> {
#[inline]
fn trace(&self, cc: gc_arena::CollectionContext) {
self.change_prototype_queue.iter().for_each(|o| o.trace(cc));
self.action_queue.iter().for_each(|o| o.trace(cc));
for queue in &self.action_queue {
queue.iter().for_each(|o| o.trace(cc));
}
}
}
@ -344,6 +345,9 @@ pub enum ActionType<'gc> {
/// Normal frame or event actions.
Normal { bytecode: SwfSlice },
/// AVM1 initialize clip event
Initialize { bytecode: SwfSlice },
/// Construct a movie with a custom class or on(construct) events
Construct {
constructor: Option<Avm1Object<'gc>>,
@ -372,6 +376,16 @@ pub enum ActionType<'gc> {
},
}
impl ActionType<'_> {
fn priority(&self) -> usize {
match self {
ActionType::Initialize { .. } => 2,
ActionType::Construct { .. } => 1,
_ => 0,
}
}
}
impl fmt::Debug for ActionType<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -379,6 +393,10 @@ impl fmt::Debug for ActionType<'_> {
.debug_struct("ActionType::Normal")
.field("bytecode", bytecode)
.finish(),
ActionType::Initialize { bytecode } => f
.debug_struct("ActionType::Initialize")
.field("bytecode", bytecode)
.finish(),
ActionType::Construct {
constructor,
events,

View File

@ -1503,12 +1503,18 @@ impl<'gc> MovieClip<'gc> {
let mut events = Vec::new();
for clip_action in mc
.clip_actions()
.iter()
.filter(|action| action.event == ClipEvent::Construct)
{
events.push(clip_action.action_data.clone());
for clip_action in mc.clip_actions().iter() {
match clip_action.event {
ClipEvent::Initialize => context.action_queue.queue_actions(
display_object,
ActionType::Initialize {
bytecode: clip_action.action_data.clone(),
},
false,
),
ClipEvent::Construct => events.push(clip_action.action_data.clone()),
_ => (),
}
}
context.action_queue.queue_actions(

View File

@ -921,7 +921,7 @@ impl Player {
match actions.action_type {
// DoAction/clip event code
ActionType::Normal { bytecode } => {
ActionType::Normal { bytecode } | ActionType::Initialize { bytecode } => {
Avm1::run_stack_frame_for_action(
actions.clip,
"[Frame]",