2019-10-27 19:46:23 +00:00
|
|
|
//! Contexts and helper types passed between functions.
|
2019-12-18 16:45:20 +00:00
|
|
|
|
2020-05-31 02:49:07 +00:00
|
|
|
use crate::avm1::globals::system::SystemProperties;
|
2020-08-22 20:16:47 +00:00
|
|
|
use crate::avm1::{Avm1, Object as Avm1Object, Timers, Value as Avm1Value};
|
|
|
|
use crate::avm2::{Avm2, Object as Avm2Object, Value as Avm2Value};
|
2019-12-18 21:25:54 +00:00
|
|
|
use crate::backend::input::InputBackend;
|
2020-07-08 18:26:00 +00:00
|
|
|
use crate::backend::locale::LocaleBackend;
|
2020-09-05 16:19:03 +00:00
|
|
|
use crate::backend::log::LogBackend;
|
2020-06-15 16:52:49 +00:00
|
|
|
use crate::backend::storage::StorageBackend;
|
2021-01-23 02:03:59 +00:00
|
|
|
use crate::backend::{
|
|
|
|
audio::{AudioBackend, AudioManager, AudioStreamHandle, SoundHandle, SoundInstanceHandle},
|
|
|
|
navigator::NavigatorBackend,
|
|
|
|
render::RenderBackend,
|
|
|
|
};
|
2021-01-24 08:16:07 +00:00
|
|
|
use crate::display_object::{EditText, SoundTransform};
|
2020-08-27 21:47:08 +00:00
|
|
|
use crate::external::ExternalInterface;
|
2020-10-29 23:09:57 +00:00
|
|
|
use crate::focus_tracker::FocusTracker;
|
2019-10-27 19:46:23 +00:00
|
|
|
use crate::library::Library;
|
2020-01-10 23:28:49 +00:00
|
|
|
use crate::loader::LoadManager;
|
2019-11-08 20:09:57 +00:00
|
|
|
use crate::player::Player;
|
2019-10-27 19:46:23 +00:00
|
|
|
use crate::prelude::*;
|
2019-11-12 20:05:18 +00:00
|
|
|
use crate::tag_utils::{SwfMovie, SwfSlice};
|
2019-10-27 19:46:23 +00:00
|
|
|
use crate::transform::TransformStack;
|
2019-12-16 21:58:31 +00:00
|
|
|
use core::fmt;
|
2020-07-28 01:27:02 +00:00
|
|
|
use gc_arena::{Collect, CollectionContext, MutationContext};
|
2020-10-11 19:10:27 +00:00
|
|
|
use instant::Instant;
|
2019-10-27 19:46:23 +00:00
|
|
|
use rand::rngs::SmallRng;
|
2020-07-08 02:06:19 +00:00
|
|
|
use std::collections::{BTreeMap, HashMap, VecDeque};
|
2019-11-08 20:09:57 +00:00
|
|
|
use std::sync::{Arc, Mutex, Weak};
|
2020-09-15 22:28:41 +00:00
|
|
|
use std::time::Duration;
|
2019-10-27 19:46:23 +00:00
|
|
|
|
|
|
|
/// `UpdateContext` holds shared data that is used by the various subsystems of Ruffle.
|
|
|
|
/// `Player` crates this when it begins a tick and passes it through the call stack to
|
|
|
|
/// children and the VM.
|
|
|
|
pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
|
|
|
/// The queue of actions that will be run after the display list updates.
|
|
|
|
/// Display objects and actions can push actions onto the queue.
|
|
|
|
pub action_queue: &'a mut ActionQueue<'gc>,
|
|
|
|
|
|
|
|
/// The background color of the Stage. Changed by the `SetBackgroundColor` SWF tag.
|
|
|
|
/// TODO: Move this into a `Stage` display object.
|
2021-01-13 05:54:15 +00:00
|
|
|
pub background_color: &'a mut Option<Color>,
|
2019-10-27 19:46:23 +00:00
|
|
|
|
|
|
|
/// The mutation context to allocate and mutate `GcCell` types.
|
|
|
|
pub gc_context: MutationContext<'gc, 'gc_context>,
|
|
|
|
|
|
|
|
/// The library containing character definitions for this SWF.
|
|
|
|
/// Used to instantiate a `DisplayObject` of a given ID.
|
|
|
|
pub library: &'a mut Library<'gc>,
|
|
|
|
|
|
|
|
/// The version of the Flash Player we are emulating.
|
|
|
|
/// TODO: This is a little confusing because this represents the player's max SWF version,
|
|
|
|
/// which is an integer (e.g. 13), but "Flash Player version" is a triplet (11.6.0), and these
|
|
|
|
/// aren't in sync. It may be better to have separate `player_swf_version` and `player_version`
|
|
|
|
/// variables.
|
|
|
|
pub player_version: u8,
|
|
|
|
|
2020-07-08 21:33:11 +00:00
|
|
|
/// Requests a that the player re-renders after this execution (e.g. due to `updateAfterEvent`).
|
|
|
|
pub needs_render: &'a mut bool,
|
|
|
|
|
2019-11-12 20:05:18 +00:00
|
|
|
/// The root SWF file.
|
|
|
|
pub swf: &'a Arc<SwfMovie>,
|
2019-10-27 19:46:23 +00:00
|
|
|
|
|
|
|
/// The audio backend, used by display objects and AVM to play audio.
|
2020-10-19 00:11:25 +00:00
|
|
|
pub audio: &'a mut dyn AudioBackend,
|
2019-10-27 19:46:23 +00:00
|
|
|
|
2021-01-23 02:03:59 +00:00
|
|
|
/// The audio manager, manging all actively playing sounds.
|
|
|
|
pub audio_manager: &'a mut AudioManager<'gc>,
|
|
|
|
|
2019-10-27 19:46:23 +00:00
|
|
|
/// The navigator backend, used by the AVM to make HTTP requests and visit webpages.
|
2019-11-08 20:09:57 +00:00
|
|
|
pub navigator: &'a mut (dyn NavigatorBackend + 'a),
|
2019-10-27 19:46:23 +00:00
|
|
|
|
|
|
|
/// The renderer, used by the display objects to draw themselves.
|
2020-05-11 07:02:49 +00:00
|
|
|
pub renderer: &'a mut dyn RenderBackend,
|
2019-10-27 19:46:23 +00:00
|
|
|
|
2019-12-18 21:25:54 +00:00
|
|
|
/// The input backend, used to detect user interactions.
|
|
|
|
pub input: &'a mut dyn InputBackend,
|
|
|
|
|
2020-06-12 18:27:20 +00:00
|
|
|
/// The storage backend, used for storing persistent state
|
|
|
|
pub storage: &'a mut dyn StorageBackend,
|
|
|
|
|
2020-07-08 18:26:00 +00:00
|
|
|
/// The locale backend, used for localisation and personalisation
|
|
|
|
pub locale: &'a mut dyn LocaleBackend,
|
|
|
|
|
2020-09-05 16:19:03 +00:00
|
|
|
/// The logging backend, used for trace output capturing
|
|
|
|
pub log: &'a mut dyn LogBackend,
|
|
|
|
|
2019-10-27 19:46:23 +00:00
|
|
|
/// The RNG, used by the AVM `RandomNumber` opcode, `Math.random(),` and `random()`.
|
|
|
|
pub rng: &'a mut SmallRng,
|
|
|
|
|
2020-02-24 07:41:55 +00:00
|
|
|
/// All loaded levels of the current player.
|
|
|
|
pub levels: &'a mut BTreeMap<u32, DisplayObject<'gc>>,
|
2019-11-13 03:00:19 +00:00
|
|
|
|
2019-12-08 18:49:23 +00:00
|
|
|
/// The display object that the mouse is currently hovering over.
|
|
|
|
pub mouse_hovered_object: Option<DisplayObject<'gc>>,
|
2019-12-16 09:51:19 +00:00
|
|
|
|
|
|
|
/// The location of the mouse when it was last over the player.
|
|
|
|
pub mouse_position: &'a (Twips, Twips),
|
2019-12-18 06:26:09 +00:00
|
|
|
|
2019-12-21 23:37:27 +00:00
|
|
|
/// The object being dragged via a `startDrag` action.
|
|
|
|
pub drag_object: &'a mut Option<crate::player::DragObject<'gc>>,
|
|
|
|
|
2019-12-18 06:26:09 +00:00
|
|
|
/// The dimensions of the stage.
|
|
|
|
pub stage_size: (Twips, Twips),
|
2019-11-08 20:09:57 +00:00
|
|
|
|
|
|
|
/// Weak reference to the player.
|
|
|
|
///
|
|
|
|
/// Recipients of an update context may upgrade the reference to ensure
|
|
|
|
/// that the player lives across I/O boundaries.
|
|
|
|
pub player: Option<Weak<Mutex<Player>>>,
|
2020-01-10 23:28:49 +00:00
|
|
|
|
|
|
|
/// The player's load manager.
|
|
|
|
///
|
|
|
|
/// This is required for asynchronous behavior, such as fetching data from
|
|
|
|
/// a URL.
|
|
|
|
pub load_manager: &'a mut LoadManager<'gc>,
|
2020-05-27 00:52:22 +00:00
|
|
|
|
|
|
|
/// The system properties
|
|
|
|
pub system: &'a mut SystemProperties,
|
2020-06-18 01:18:40 +00:00
|
|
|
|
|
|
|
/// The current instance ID. Used to generate default `instanceN` names.
|
|
|
|
pub instance_counter: &'a mut i32,
|
2020-06-22 00:59:38 +00:00
|
|
|
|
|
|
|
/// Shared objects cache
|
2020-08-22 20:16:47 +00:00
|
|
|
pub shared_objects: &'a mut HashMap<String, Avm1Object<'gc>>,
|
2020-06-23 04:07:27 +00:00
|
|
|
|
|
|
|
/// Text fields with unbound variable bindings.
|
|
|
|
pub unbound_text_fields: &'a mut Vec<EditText<'gc>>,
|
2020-07-08 02:06:19 +00:00
|
|
|
|
|
|
|
/// Timed callbacks created with `setInterval`/`setTimeout`.
|
|
|
|
pub timers: &'a mut Timers<'gc>,
|
2020-07-28 00:57:42 +00:00
|
|
|
|
|
|
|
/// The AVM1 global state.
|
|
|
|
pub avm1: &'a mut Avm1<'gc>,
|
2020-07-28 03:19:43 +00:00
|
|
|
|
|
|
|
/// The AVM2 global state.
|
|
|
|
pub avm2: &'a mut Avm2<'gc>,
|
2020-08-27 21:47:08 +00:00
|
|
|
|
2020-11-11 09:55:46 +00:00
|
|
|
/// External interface for (for example) JavaScript <-> ActionScript interaction
|
2020-08-27 22:16:53 +00:00
|
|
|
pub external_interface: &'a mut ExternalInterface<'gc>,
|
2020-09-15 22:28:41 +00:00
|
|
|
|
|
|
|
/// The instant at which the current update started.
|
|
|
|
pub update_start: Instant,
|
|
|
|
|
|
|
|
/// The maximum amount of time that can be called before a `Error::ExecutionTimeout`
|
|
|
|
/// is raised. This defaults to 15 seconds but can be changed.
|
|
|
|
pub max_execution_duration: Duration,
|
2020-10-29 23:09:57 +00:00
|
|
|
|
|
|
|
/// A tracker for the current keyboard focused element
|
|
|
|
pub focus_tracker: FocusTracker<'gc>,
|
2021-01-04 20:19:20 +00:00
|
|
|
|
|
|
|
/// How many times getTimer() was called so far. Used to detect busy-loops.
|
|
|
|
pub times_get_time_called: u32,
|
|
|
|
|
|
|
|
/// This frame's current fake time offset, used to pretend passage of time in time functions
|
|
|
|
pub time_offset: &'a mut u32,
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-23 02:03:59 +00:00
|
|
|
impl<'a, 'gc, 'gc_context> UpdateContext<'a, 'gc, 'gc_context> {
|
|
|
|
pub fn update_sounds(&mut self) {
|
2021-01-23 04:46:27 +00:00
|
|
|
let removed = self
|
|
|
|
.audio_manager
|
|
|
|
.update_sounds(self.audio, self.gc_context);
|
|
|
|
for sound in removed {
|
|
|
|
if let Some(object) = sound.avm1_object {
|
|
|
|
self.action_queue.queue_actions(
|
|
|
|
*self.levels.get(&0).unwrap(),
|
|
|
|
ActionType::Method {
|
|
|
|
object: object.into(),
|
|
|
|
name: "onSoundComplete",
|
|
|
|
args: vec![],
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-01-23 02:03:59 +00:00
|
|
|
}
|
|
|
|
|
2021-01-24 08:16:07 +00:00
|
|
|
pub fn global_sound_transform(&self) -> &SoundTransform {
|
|
|
|
self.audio_manager.global_sound_transform()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_global_sound_transform(&mut self, sound_transform: SoundTransform) {
|
|
|
|
self.audio_manager
|
|
|
|
.set_global_sound_transform(self.audio, sound_transform);
|
|
|
|
}
|
|
|
|
|
2021-01-23 02:03:59 +00:00
|
|
|
pub fn start_sound(
|
|
|
|
&mut self,
|
|
|
|
sound: SoundHandle,
|
|
|
|
settings: &swf::SoundInfo,
|
|
|
|
owner: Option<DisplayObject<'gc>>,
|
|
|
|
avm1_object: Option<crate::avm1::SoundObject<'gc>>,
|
|
|
|
) -> Option<SoundInstanceHandle> {
|
|
|
|
self.audio_manager
|
|
|
|
.start_sound(self.audio, sound, settings, owner, avm1_object)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop_sound(&mut self, instance: SoundInstanceHandle) {
|
|
|
|
self.audio_manager.stop_sound(self.audio, instance)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop_sounds_with_handle(&mut self, sound: SoundHandle) {
|
|
|
|
self.audio_manager
|
|
|
|
.stop_sounds_with_handle(self.audio, sound)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop_sounds_with_display_object(&mut self, display_object: DisplayObject<'gc>) {
|
|
|
|
self.audio_manager
|
|
|
|
.stop_sounds_with_display_object(self.audio, display_object)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop_all_sounds(&mut self) {
|
|
|
|
self.audio_manager.stop_all_sounds(self.audio)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_sound_playing_with_handle(&mut self, sound: SoundHandle) -> bool {
|
|
|
|
self.audio_manager.is_sound_playing_with_handle(sound)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start_stream(
|
|
|
|
&mut self,
|
|
|
|
clip_id: swf::CharacterId,
|
|
|
|
frame: u16,
|
|
|
|
data: crate::tag_utils::SwfSlice,
|
|
|
|
stream_info: &swf::SoundStreamHead,
|
|
|
|
) -> Option<AudioStreamHandle> {
|
|
|
|
self.audio_manager
|
|
|
|
.start_stream(self.audio, clip_id, frame, data, stream_info)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop_stream(&mut self, handle: AudioStreamHandle) {
|
|
|
|
self.audio_manager.stop_stream(self.audio, handle)
|
|
|
|
}
|
2021-01-24 08:16:07 +00:00
|
|
|
|
|
|
|
pub fn update_sound_transforms(&mut self) {
|
|
|
|
self.audio_manager.update_sound_transforms(self.audio)
|
|
|
|
}
|
2021-01-23 02:03:59 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 01:27:02 +00:00
|
|
|
unsafe impl<'a, 'gc, 'gc_context> Collect for UpdateContext<'a, 'gc, 'gc_context> {
|
|
|
|
fn trace(&self, cc: CollectionContext) {
|
|
|
|
self.action_queue.trace(cc);
|
|
|
|
self.background_color.trace(cc);
|
|
|
|
self.library.trace(cc);
|
|
|
|
self.player_version.trace(cc);
|
|
|
|
self.needs_render.trace(cc);
|
|
|
|
self.swf.trace(cc);
|
|
|
|
self.audio.trace(cc);
|
2021-01-23 02:03:59 +00:00
|
|
|
self.audio_manager.trace(cc);
|
2020-07-28 01:27:02 +00:00
|
|
|
self.navigator.trace(cc);
|
|
|
|
self.renderer.trace(cc);
|
|
|
|
self.input.trace(cc);
|
|
|
|
self.storage.trace(cc);
|
|
|
|
self.rng.trace(cc);
|
|
|
|
self.levels.trace(cc);
|
|
|
|
self.mouse_hovered_object.trace(cc);
|
|
|
|
self.mouse_position.trace(cc);
|
|
|
|
self.drag_object.trace(cc);
|
|
|
|
self.load_manager.trace(cc);
|
|
|
|
self.system.trace(cc);
|
|
|
|
self.instance_counter.trace(cc);
|
|
|
|
self.shared_objects.trace(cc);
|
|
|
|
self.unbound_text_fields.trace(cc);
|
|
|
|
self.timers.trace(cc);
|
|
|
|
self.avm1.trace(cc);
|
2020-07-28 03:19:43 +00:00
|
|
|
self.avm2.trace(cc);
|
2020-10-29 23:09:57 +00:00
|
|
|
self.focus_tracker.trace(cc);
|
2020-07-28 01:27:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'gc, 'gc_context> UpdateContext<'a, 'gc, 'gc_context> {
|
|
|
|
/// Transform a borrowed update context into an owned update context with
|
|
|
|
/// a shorter internal lifetime.
|
|
|
|
///
|
|
|
|
/// This is particularly useful for structures that may wish to hold an
|
2020-11-11 09:55:46 +00:00
|
|
|
/// update context without adding further lifetimes for its borrowing.
|
2020-07-28 01:27:02 +00:00
|
|
|
/// Please note that you will not be able to use the original update
|
|
|
|
/// context until this reborrowed copy has fallen out of scope.
|
2020-07-26 02:11:38 +00:00
|
|
|
pub fn reborrow<'b>(&'b mut self) -> UpdateContext<'b, 'gc, 'gc_context>
|
2020-07-28 01:27:02 +00:00
|
|
|
where
|
|
|
|
'a: 'b,
|
|
|
|
{
|
|
|
|
UpdateContext {
|
|
|
|
action_queue: self.action_queue,
|
|
|
|
background_color: self.background_color,
|
|
|
|
gc_context: self.gc_context,
|
|
|
|
library: self.library,
|
|
|
|
player_version: self.player_version,
|
|
|
|
needs_render: self.needs_render,
|
|
|
|
swf: self.swf,
|
|
|
|
audio: self.audio,
|
2021-01-23 02:03:59 +00:00
|
|
|
audio_manager: self.audio_manager,
|
2020-07-28 01:27:02 +00:00
|
|
|
navigator: self.navigator,
|
|
|
|
renderer: self.renderer,
|
2020-07-08 18:26:00 +00:00
|
|
|
locale: self.locale,
|
2020-09-05 16:19:03 +00:00
|
|
|
log: self.log,
|
2020-07-28 01:27:02 +00:00
|
|
|
input: self.input,
|
|
|
|
storage: self.storage,
|
|
|
|
rng: self.rng,
|
|
|
|
levels: self.levels,
|
|
|
|
mouse_hovered_object: self.mouse_hovered_object,
|
|
|
|
mouse_position: self.mouse_position,
|
|
|
|
drag_object: self.drag_object,
|
|
|
|
stage_size: self.stage_size,
|
2020-07-26 02:11:38 +00:00
|
|
|
player: self.player.clone(),
|
2020-07-28 01:27:02 +00:00
|
|
|
load_manager: self.load_manager,
|
|
|
|
system: self.system,
|
|
|
|
instance_counter: self.instance_counter,
|
|
|
|
shared_objects: self.shared_objects,
|
|
|
|
unbound_text_fields: self.unbound_text_fields,
|
|
|
|
timers: self.timers,
|
|
|
|
avm1: self.avm1,
|
2020-07-28 03:19:43 +00:00
|
|
|
avm2: self.avm2,
|
2020-08-27 21:47:08 +00:00
|
|
|
external_interface: self.external_interface,
|
2020-09-15 22:28:41 +00:00
|
|
|
update_start: self.update_start,
|
|
|
|
max_execution_duration: self.max_execution_duration,
|
2020-10-29 23:09:57 +00:00
|
|
|
focus_tracker: self.focus_tracker,
|
2021-01-04 20:19:20 +00:00
|
|
|
times_get_time_called: self.times_get_time_called,
|
|
|
|
time_offset: self.time_offset,
|
2020-07-28 01:27:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-27 19:46:23 +00:00
|
|
|
/// A queued ActionScript call.
|
|
|
|
pub struct QueuedActions<'gc> {
|
|
|
|
/// The movie clip this ActionScript is running on.
|
2019-12-07 02:29:36 +00:00
|
|
|
pub clip: DisplayObject<'gc>,
|
2019-10-27 19:46:23 +00:00
|
|
|
|
2019-12-16 02:10:28 +00:00
|
|
|
/// The type of action this is, along with the corresponding bytecode/method data.
|
2019-12-16 21:58:31 +00:00
|
|
|
pub action_type: ActionType<'gc>,
|
2019-12-16 02:10:28 +00:00
|
|
|
|
|
|
|
/// Whether this is an unload action, which can still run if the clip is removed.
|
|
|
|
pub is_unload: bool,
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 21:58:31 +00:00
|
|
|
unsafe impl<'gc> Collect for QueuedActions<'gc> {
|
|
|
|
#[inline]
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
|
|
|
self.clip.trace(cc);
|
|
|
|
self.action_type.trace(cc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-27 19:46:23 +00:00
|
|
|
/// Action and gotos need to be queued up to execute at the end of the frame.
|
|
|
|
pub struct ActionQueue<'gc> {
|
2020-11-09 07:43:41 +00:00
|
|
|
/// Each priority is kept in a separate bucket.
|
|
|
|
action_queue: Vec<VecDeque<QueuedActions<'gc>>>,
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> ActionQueue<'gc> {
|
|
|
|
const DEFAULT_CAPACITY: usize = 32;
|
2020-11-09 07:43:41 +00:00
|
|
|
const NUM_PRIORITIES: usize = 3;
|
2019-10-27 19:46:23 +00:00
|
|
|
|
|
|
|
/// Crates a new `ActionQueue` with an empty queue.
|
|
|
|
pub fn new() -> Self {
|
2020-11-09 07:43:41 +00:00
|
|
|
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))
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
2020-11-09 07:43:41 +00:00
|
|
|
Self { action_queue }
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Queues ActionScript to run for the given movie clip.
|
|
|
|
/// `actions` is the slice of ActionScript bytecode to run.
|
|
|
|
/// The actions will be skipped if the clip is removed before the actions run.
|
2019-12-01 20:05:19 +00:00
|
|
|
pub fn queue_actions(
|
|
|
|
&mut self,
|
|
|
|
clip: DisplayObject<'gc>,
|
2019-12-16 21:58:31 +00:00
|
|
|
action_type: ActionType<'gc>,
|
2019-12-16 02:10:28 +00:00
|
|
|
is_unload: bool,
|
2019-12-01 20:05:19 +00:00
|
|
|
) {
|
2020-11-09 07:43:41 +00:00
|
|
|
let priority = action_type.priority();
|
|
|
|
let action = 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)
|
2020-04-25 09:05:01 +00:00
|
|
|
}
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
2020-04-12 18:50:34 +00:00
|
|
|
/// Sorts and drains the actions from the queue.
|
2020-04-25 09:05:01 +00:00
|
|
|
pub fn pop_action(&mut self) -> Option<QueuedActions<'gc>> {
|
2020-11-09 07:43:41 +00:00
|
|
|
for queue in self.action_queue.iter_mut().rev() {
|
|
|
|
let action = queue.pop_front();
|
|
|
|
if action.is_some() {
|
|
|
|
return action;
|
|
|
|
}
|
2020-04-25 09:05:01 +00:00
|
|
|
}
|
2020-11-09 07:43:41 +00:00
|
|
|
None
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Default for ActionQueue<'gc> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> Collect for ActionQueue<'gc> {
|
|
|
|
#[inline]
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
2020-11-09 07:43:41 +00:00
|
|
|
for queue in &self.action_queue {
|
|
|
|
queue.iter().for_each(|o| o.trace(cc));
|
|
|
|
}
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Shared data used during rendering.
|
|
|
|
/// `Player` creates this when it renders a frame and passes it down to display objects.
|
|
|
|
pub struct RenderContext<'a, 'gc> {
|
|
|
|
/// The renderer, used by the display objects to draw themselves.
|
|
|
|
pub renderer: &'a mut dyn RenderBackend,
|
|
|
|
|
|
|
|
/// The library, which provides access to fonts and other definitions when rendering.
|
|
|
|
pub library: &'a Library<'gc>,
|
|
|
|
|
|
|
|
/// The transform stack controls the matrix and color transform as we traverse the display hierarchy.
|
|
|
|
pub transform_stack: &'a mut TransformStack,
|
|
|
|
/// The bounds of the current viewport in twips. Used for culling.
|
|
|
|
pub view_bounds: BoundingBox,
|
|
|
|
|
|
|
|
/// The stack of clip depths, used in masking.
|
|
|
|
pub clip_depth_stack: Vec<Depth>,
|
2020-10-25 04:08:25 +00:00
|
|
|
|
|
|
|
/// Whether to allow pushing a new mask. A masker-inside-a-masker does not work in Flash, instead
|
|
|
|
/// causing the inner mask to be included as part of the outer mask. Maskee-inside-a-maskee works as one expects.
|
|
|
|
pub allow_mask: bool,
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
2019-12-01 20:05:19 +00:00
|
|
|
|
|
|
|
/// The type of action being run.
|
2019-12-16 21:58:31 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum ActionType<'gc> {
|
2019-12-01 20:05:19 +00:00
|
|
|
/// Normal frame or event actions.
|
2019-12-16 02:10:28 +00:00
|
|
|
Normal { bytecode: SwfSlice },
|
2019-12-01 20:05:19 +00:00
|
|
|
|
2020-11-09 07:43:41 +00:00
|
|
|
/// AVM1 initialize clip event
|
|
|
|
Initialize { bytecode: SwfSlice },
|
|
|
|
|
2020-04-20 12:27:23 +00:00
|
|
|
/// Construct a movie with a custom class or on(construct) events
|
|
|
|
Construct {
|
2020-08-22 20:16:47 +00:00
|
|
|
constructor: Option<Avm1Object<'gc>>,
|
2020-04-20 12:27:23 +00:00
|
|
|
events: Vec<SwfSlice>,
|
|
|
|
},
|
2020-04-12 18:50:34 +00:00
|
|
|
|
2019-12-16 02:10:28 +00:00
|
|
|
/// An event handler method, e.g. `onEnterFrame`.
|
2020-01-16 19:20:07 +00:00
|
|
|
Method {
|
2020-08-22 20:16:47 +00:00
|
|
|
object: Avm1Object<'gc>,
|
2020-01-16 19:20:07 +00:00
|
|
|
name: &'static str,
|
2020-08-22 20:16:47 +00:00
|
|
|
args: Vec<Avm1Value<'gc>>,
|
2020-01-16 19:20:07 +00:00
|
|
|
},
|
2019-12-16 21:58:31 +00:00
|
|
|
|
2019-12-18 16:45:20 +00:00
|
|
|
/// A system listener method,
|
|
|
|
NotifyListeners {
|
2020-07-28 20:49:41 +00:00
|
|
|
listener: &'static str,
|
2019-12-18 16:45:20 +00:00
|
|
|
method: &'static str,
|
2020-08-22 20:16:47 +00:00
|
|
|
args: Vec<Avm1Value<'gc>>,
|
|
|
|
},
|
|
|
|
|
|
|
|
/// An AVM2 callable, e.g. a frame script or event handler.
|
|
|
|
Callable2 {
|
|
|
|
callable: Avm2Object<'gc>,
|
|
|
|
reciever: Option<Avm2Object<'gc>>,
|
|
|
|
args: Vec<Avm2Value<'gc>>,
|
2019-12-16 21:58:31 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-11-09 07:43:41 +00:00
|
|
|
impl ActionType<'_> {
|
|
|
|
fn priority(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
ActionType::Initialize { .. } => 2,
|
|
|
|
ActionType::Construct { .. } => 1,
|
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 21:58:31 +00:00
|
|
|
impl fmt::Debug for ActionType<'_> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
ActionType::Normal { bytecode } => f
|
|
|
|
.debug_struct("ActionType::Normal")
|
|
|
|
.field("bytecode", bytecode)
|
|
|
|
.finish(),
|
2020-11-09 07:43:41 +00:00
|
|
|
ActionType::Initialize { bytecode } => f
|
|
|
|
.debug_struct("ActionType::Initialize")
|
|
|
|
.field("bytecode", bytecode)
|
|
|
|
.finish(),
|
2020-04-20 12:27:23 +00:00
|
|
|
ActionType::Construct {
|
|
|
|
constructor,
|
|
|
|
events,
|
|
|
|
} => f
|
|
|
|
.debug_struct("ActionType::Construct")
|
2020-04-12 18:50:34 +00:00
|
|
|
.field("constructor", constructor)
|
2020-04-20 12:27:23 +00:00
|
|
|
.field("events", events)
|
2020-04-12 18:50:34 +00:00
|
|
|
.finish(),
|
2020-01-16 19:20:07 +00:00
|
|
|
ActionType::Method { object, name, args } => f
|
2019-12-16 21:58:31 +00:00
|
|
|
.debug_struct("ActionType::Method")
|
2020-01-16 19:20:07 +00:00
|
|
|
.field("object", object)
|
2019-12-16 21:58:31 +00:00
|
|
|
.field("name", name)
|
2020-01-16 19:20:07 +00:00
|
|
|
.field("args", args)
|
2019-12-16 21:58:31 +00:00
|
|
|
.finish(),
|
2019-12-18 16:45:20 +00:00
|
|
|
ActionType::NotifyListeners {
|
|
|
|
listener,
|
|
|
|
method,
|
|
|
|
args,
|
|
|
|
} => f
|
|
|
|
.debug_struct("ActionType::NotifyListeners")
|
|
|
|
.field("listener", listener)
|
|
|
|
.field("method", method)
|
2019-12-16 21:58:31 +00:00
|
|
|
.field("args", args)
|
|
|
|
.finish(),
|
2020-08-22 20:16:47 +00:00
|
|
|
ActionType::Callable2 {
|
|
|
|
callable,
|
|
|
|
reciever,
|
|
|
|
args,
|
|
|
|
} => f
|
|
|
|
.debug_struct("ActionType::Callable2")
|
|
|
|
.field("callable", callable)
|
|
|
|
.field("reciever", reciever)
|
|
|
|
.field("args", args)
|
|
|
|
.finish(),
|
2019-12-16 21:58:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> Collect for ActionType<'gc> {
|
|
|
|
#[inline]
|
|
|
|
fn trace(&self, cc: gc_arena::CollectionContext) {
|
2020-04-12 18:50:34 +00:00
|
|
|
match self {
|
2020-04-20 12:27:23 +00:00
|
|
|
ActionType::Construct { constructor, .. } => {
|
2020-04-12 18:50:34 +00:00
|
|
|
constructor.trace(cc);
|
|
|
|
}
|
|
|
|
ActionType::Method { object, args, .. } => {
|
|
|
|
object.trace(cc);
|
|
|
|
args.trace(cc);
|
|
|
|
}
|
|
|
|
ActionType::NotifyListeners { args, .. } => {
|
|
|
|
args.trace(cc);
|
|
|
|
}
|
|
|
|
_ => {}
|
2019-12-16 21:58:31 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-01 20:05:19 +00:00
|
|
|
}
|