2019-10-27 19:46:23 +00:00
|
|
|
//! Contexts and helper types passed between functions.
|
|
|
|
use crate::avm1;
|
2019-12-18 16:45:20 +00:00
|
|
|
|
|
|
|
use crate::avm1::listeners::SystemListener;
|
2020-01-16 19:20:07 +00:00
|
|
|
use crate::avm1::{Object, Value};
|
2019-12-18 21:25:54 +00:00
|
|
|
use crate::backend::input::InputBackend;
|
2019-10-27 19:46:23 +00:00
|
|
|
use crate::backend::{audio::AudioBackend, navigator::NavigatorBackend, render::RenderBackend};
|
|
|
|
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;
|
2019-10-27 19:46:23 +00:00
|
|
|
use gc_arena::{Collect, MutationContext};
|
|
|
|
use rand::rngs::SmallRng;
|
2019-12-02 02:00:01 +00:00
|
|
|
use std::collections::BTreeMap;
|
2020-04-25 09:05:01 +00:00
|
|
|
use std::collections::VecDeque;
|
2019-11-08 20:09:57 +00:00
|
|
|
use std::sync::{Arc, Mutex, Weak};
|
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.
|
|
|
|
pub background_color: &'a mut Color,
|
|
|
|
|
|
|
|
/// The mutation context to allocate and mutate `GcCell` types.
|
|
|
|
pub gc_context: MutationContext<'gc, 'gc_context>,
|
|
|
|
|
|
|
|
/// The time elapsed since this SWF started executing.
|
|
|
|
/// Used by AVM1 `GetTime` action and `getTimer` function.
|
|
|
|
pub global_time: u64,
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
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.
|
2019-11-08 20:09:57 +00:00
|
|
|
pub audio: &'a mut (dyn AudioBackend + 'a),
|
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,
|
|
|
|
|
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-10-17 02:31:41 +00:00
|
|
|
/// The current set of system-specified prototypes to use when constructing
|
|
|
|
/// new built-in objects.
|
|
|
|
pub system_prototypes: avm1::SystemPrototypes<'gc>,
|
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>,
|
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-04-25 09:05:01 +00:00
|
|
|
change_prototype_queue: VecDeque<QueuedActions<'gc>>,
|
|
|
|
action_queue: VecDeque<QueuedActions<'gc>>,
|
2019-10-27 19:46:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> ActionQueue<'gc> {
|
|
|
|
const DEFAULT_CAPACITY: usize = 32;
|
|
|
|
|
|
|
|
/// Crates a new `ActionQueue` with an empty queue.
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2020-04-25 09:05:01 +00:00
|
|
|
change_prototype_queue: VecDeque::with_capacity(Self::DEFAULT_CAPACITY),
|
|
|
|
action_queue: VecDeque::with_capacity(Self::DEFAULT_CAPACITY),
|
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-04-25 09:05:01 +00:00
|
|
|
// Prototype change goes a higher priority queue.
|
2020-04-20 12:27:23 +00:00
|
|
|
if let ActionType::Construct { .. } = action_type {
|
2020-04-25 09:05:01 +00:00
|
|
|
self.change_prototype_queue.push_back(QueuedActions {
|
|
|
|
clip,
|
|
|
|
action_type,
|
|
|
|
is_unload,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
self.action_queue.push_back(QueuedActions {
|
|
|
|
clip,
|
|
|
|
action_type,
|
|
|
|
is_unload,
|
|
|
|
})
|
|
|
|
}
|
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>> {
|
|
|
|
if !self.change_prototype_queue.is_empty() {
|
|
|
|
self.change_prototype_queue.pop_front()
|
|
|
|
} else {
|
|
|
|
self.action_queue.pop_front()
|
|
|
|
}
|
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-04-25 09:05:01 +00:00
|
|
|
self.change_prototype_queue.iter().for_each(|o| o.trace(cc));
|
|
|
|
self.action_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>,
|
|
|
|
}
|
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-04-20 12:27:23 +00:00
|
|
|
/// Construct a movie with a custom class or on(construct) events
|
|
|
|
Construct {
|
|
|
|
constructor: Option<Object<'gc>>,
|
|
|
|
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 {
|
|
|
|
object: Object<'gc>,
|
|
|
|
name: &'static str,
|
|
|
|
args: Vec<Value<'gc>>,
|
|
|
|
},
|
2019-12-16 21:58:31 +00:00
|
|
|
|
2019-12-18 16:45:20 +00:00
|
|
|
/// A system listener method,
|
|
|
|
NotifyListeners {
|
|
|
|
listener: SystemListener,
|
|
|
|
method: &'static str,
|
2019-12-16 21:58:31 +00:00
|
|
|
args: Vec<Value<'gc>>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
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-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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|