diff --git a/core/src/avm2/globals/flash/net/NetConnection.as b/core/src/avm2/globals/flash/net/NetConnection.as index fc1903836..161e86795 100644 --- a/core/src/avm2/globals/flash/net/NetConnection.as +++ b/core/src/avm2/globals/flash/net/NetConnection.as @@ -3,6 +3,7 @@ package flash.net { import flash.errors.IOError; import __ruffle__.stub_method; + [Ruffle(InstanceAllocator)] public class NetConnection extends EventDispatcher { public static var defaultObjectEncoding:uint = 3; diff --git a/core/src/avm2/globals/flash/net/net_connection.rs b/core/src/avm2/globals/flash/net/net_connection.rs index 8948b8602..2a67649ea 100644 --- a/core/src/avm2/globals/flash/net/net_connection.rs +++ b/core/src/avm2/globals/flash/net/net_connection.rs @@ -1,5 +1,8 @@ +pub use crate::avm2::object::net_connection_allocator; +use crate::avm2::object::TObject; +use crate::net_connection::NetConnections; use crate::{ - avm2::{Activation, Avm2, Error, EventObject, Object, Value}, + avm2::{Activation, Error, Object, Value}, avm2_stub_method, }; @@ -8,16 +11,12 @@ pub fn connect<'gc>( this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { + let connection = this + .as_net_connection() + .expect("Must be NetConnection object"); + if let Value::Null = args[0] { - let event = EventObject::net_status_event( - activation, - "netStatus", - vec![ - ("code", "NetConnection.Connect.Success"), - ("level", "status"), - ], - ); - Avm2::dispatch_event(&mut activation.context, event, this); + NetConnections::connect_to_local(&mut activation.context, connection); return Ok(Value::Undefined); } avm2_stub_method!( diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 7cc62c405..1b65e0211 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -45,6 +45,7 @@ mod function_object; mod index_buffer_3d_object; mod loaderinfo_object; mod namespace_object; +mod net_connection_object; mod netstream_object; mod primitive_object; mod program_3d_object; @@ -97,6 +98,9 @@ pub use crate::avm2::object::loaderinfo_object::{ pub use crate::avm2::object::namespace_object::{ namespace_allocator, NamespaceObject, NamespaceObjectWeak, }; +pub use crate::avm2::object::net_connection_object::{ + net_connection_allocator, NetConnectionObject, NetConnectionObjectWeak, +}; pub use crate::avm2::object::netstream_object::{ netstream_allocator, NetStreamObject, NetStreamObjectWeak, }; @@ -175,6 +179,7 @@ use crate::font::Font; TextureObject(TextureObject<'gc>), Program3DObject(Program3DObject<'gc>), NetStreamObject(NetStreamObject<'gc>), + NetConnectionObject(NetConnectionObject<'gc>), ShaderDataObject(ShaderDataObject<'gc>), SocketObject(SocketObject<'gc>), FontObject(FontObject<'gc>) @@ -1385,6 +1390,10 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy None } + fn as_net_connection(self) -> Option> { + None + } + fn as_socket(&self) -> Option> { None } @@ -1432,6 +1441,7 @@ impl<'gc> Object<'gc> { Self::TextureObject(o) => WeakObject::TextureObject(TextureObjectWeak(Gc::downgrade(o.0))), Self::Program3DObject(o) => WeakObject::Program3DObject(Program3DObjectWeak(Gc::downgrade(o.0))), Self::NetStreamObject(o) => WeakObject::NetStreamObject(NetStreamObjectWeak(GcCell::downgrade(o.0))), + Self::NetConnectionObject(o) => WeakObject::NetConnectionObject(NetConnectionObjectWeak(Gc::downgrade(o.0))), Self::ShaderDataObject(o) => WeakObject::ShaderDataObject(ShaderDataObjectWeak(Gc::downgrade(o.0))), Self::SocketObject(o) => WeakObject::SocketObject(SocketObjectWeak(Gc::downgrade(o.0))), Self::FontObject(o) => WeakObject::FontObject(FontObjectWeak(GcCell::downgrade(o.0))), @@ -1489,6 +1499,7 @@ pub enum WeakObject<'gc> { TextureObject(TextureObjectWeak<'gc>), Program3DObject(Program3DObjectWeak<'gc>), NetStreamObject(NetStreamObjectWeak<'gc>), + NetConnectionObject(NetConnectionObjectWeak<'gc>), ShaderDataObject(ShaderDataObjectWeak<'gc>), SocketObject(SocketObjectWeak<'gc>), FontObject(FontObjectWeak<'gc>), @@ -1529,6 +1540,7 @@ impl<'gc> WeakObject<'gc> { Self::TextureObject(o) => TextureObject(o.0.upgrade(mc)?).into(), Self::Program3DObject(o) => Program3DObject(o.0.upgrade(mc)?).into(), Self::NetStreamObject(o) => NetStreamObject(o.0.upgrade(mc)?).into(), + Self::NetConnectionObject(o) => NetConnectionObject(o.0.upgrade(mc)?).into(), Self::ShaderDataObject(o) => ShaderDataObject(o.0.upgrade(mc)?).into(), Self::SocketObject(o) => SocketObject(o.0.upgrade(mc)?).into(), Self::FontObject(o) => FontObject(o.0.upgrade(mc)?).into(), diff --git a/core/src/avm2/object/net_connection_object.rs b/core/src/avm2/object/net_connection_object.rs new file mode 100644 index 000000000..3593ef403 --- /dev/null +++ b/core/src/avm2/object/net_connection_object.rs @@ -0,0 +1,85 @@ +//! Object representation for NetConnection + +use crate::avm2::activation::Activation; +use crate::avm2::object::script_object::ScriptObjectData; +use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject}; +use crate::avm2::value::Value; +use crate::avm2::Error; +use crate::net_connection::NetConnectionHandle; +use gc_arena::barrier::unlock; +use gc_arena::lock::RefLock; +use gc_arena::{Collect, Gc, GcWeak, Mutation}; +use std::cell::{Cell, Ref, RefMut}; +use std::fmt; +use std::fmt::Debug; + +pub fn net_connection_allocator<'gc>( + class: ClassObject<'gc>, + activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + let base = ScriptObjectData::new(class).into(); + let this: Object<'gc> = NetConnectionObject(Gc::new( + activation.context.gc_context, + NetConnectionObjectData { + base, + handle: Cell::new(None), + }, + )) + .into(); + + Ok(this) +} + +#[derive(Clone, Collect, Copy)] +#[collect(no_drop)] +pub struct NetConnectionObject<'gc>(pub Gc<'gc, NetConnectionObjectData<'gc>>); + +#[derive(Collect, Clone, Copy, Debug)] +#[collect(no_drop)] +pub struct NetConnectionObjectWeak<'gc>(pub GcWeak<'gc, NetConnectionObjectData<'gc>>); + +#[derive(Collect)] +#[collect(no_drop)] +pub struct NetConnectionObjectData<'gc> { + base: RefLock>, + #[collect(require_static)] + handle: Cell>, +} + +impl<'gc> TObject<'gc> for NetConnectionObject<'gc> { + fn base(&self) -> Ref> { + self.0.base.borrow() + } + + fn base_mut(&self, mc: &Mutation<'gc>) -> RefMut> { + unlock!(Gc::write(mc, self.0), NetConnectionObjectData, base).borrow_mut() + } + + fn as_ptr(&self) -> *const ObjectPtr { + Gc::as_ptr(self.0) as *const ObjectPtr + } + + fn value_of(&self, _mc: &Mutation<'gc>) -> Result, Error<'gc>> { + Ok(Value::Object(Object::from(*self))) + } + + fn as_net_connection(self) -> Option> { + Some(self) + } +} + +impl<'gc> NetConnectionObject<'gc> { + pub fn handle(&self) -> Option { + self.0.handle.get() + } + + pub fn set_handle(&self, handle: Option) -> Option { + self.0.handle.replace(handle) + } +} + +impl<'gc> Debug for NetConnectionObject<'gc> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "NetConnectionObject") + } +} diff --git a/core/src/context.rs b/core/src/context.rs index 2aba056c3..4c649f42b 100644 --- a/core/src/context.rs +++ b/core/src/context.rs @@ -18,6 +18,7 @@ use crate::focus_tracker::FocusTracker; use crate::frame_lifecycle::FramePhase; use crate::library::Library; use crate::loader::LoadManager; +use crate::net_connection::NetConnections; use crate::player::Player; use crate::prelude::*; use crate::socket::Sockets; @@ -227,6 +228,9 @@ pub struct UpdateContext<'a, 'gc> { pub sockets: &'a mut Sockets<'gc>, + /// List of active NetConnection instances. + pub net_connections: &'a mut NetConnections<'gc>, + /// Dynamic root for allowing handles to GC objects to exist outside of the GC. pub dynamic_root: gc_arena::DynamicRootSet<'gc>, } @@ -394,6 +398,7 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> { frame_phase: self.frame_phase, stream_manager: self.stream_manager, sockets: self.sockets, + net_connections: self.net_connections, dynamic_root: self.dynamic_root, } } diff --git a/core/src/lib.rs b/core/src/lib.rs index ebc6beccd..77db3d9e0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -38,6 +38,7 @@ mod library; pub mod limits; pub mod loader; mod locale; +mod net_connection; pub mod pixel_bender; mod player; mod prelude; diff --git a/core/src/net_connection.rs b/core/src/net_connection.rs new file mode 100644 index 000000000..bca7a8a6f --- /dev/null +++ b/core/src/net_connection.rs @@ -0,0 +1,105 @@ +use crate::avm2::object::NetConnectionObject as Avm2NetConnectionObject; +use crate::avm2::{Activation as Avm2Activation, Avm2, EventObject as Avm2EventObject}; +use crate::context::UpdateContext; +use gc_arena::Collect; +use generational_arena::{Arena, Index}; + +pub type NetConnectionHandle = Index; + +#[derive(Copy, Clone, Collect)] +#[collect(no_drop)] +pub enum NetConnectionObject<'gc> { + Avm2(Avm2NetConnectionObject<'gc>), +} + +impl<'gc> NetConnectionObject<'gc> { + pub fn set_handle(&self, handle: Option) -> Option { + match self { + NetConnectionObject::Avm2(object) => object.set_handle(handle), + } + } +} + +impl<'gc> From> for NetConnectionObject<'gc> { + fn from(value: Avm2NetConnectionObject<'gc>) -> Self { + NetConnectionObject::Avm2(value) + } +} + +/// Manages the collection of NetConnections. +pub struct NetConnections<'gc> { + connections: Arena>, +} + +unsafe impl<'gc> Collect for NetConnections<'gc> { + fn trace(&self, cc: &gc_arena::Collection) { + for (_, connection) in self.connections.iter() { + connection.trace(cc) + } + } +} + +impl<'gc> Default for NetConnections<'gc> { + fn default() -> Self { + Self { + connections: Arena::new(), + } + } +} + +impl<'gc> NetConnections<'gc> { + pub fn connect_to_local>>( + context: &mut UpdateContext<'_, 'gc>, + target: O, + ) { + let target = target.into(); + let connection = NetConnection { object: target }; + let handle = context.net_connections.connections.insert(connection); + + if let Some(existing_handle) = target.set_handle(Some(handle)) { + NetConnections::close(context, existing_handle) + } + + match target { + NetConnectionObject::Avm2(object) => { + let mut activation = Avm2Activation::from_nothing(context.reborrow()); + let event = Avm2EventObject::net_status_event( + &mut activation, + "netStatus", + vec![ + ("code", "NetConnection.Connect.Success"), + ("level", "status"), + ], + ); + Avm2::dispatch_event(&mut activation.context, event, object.into()); + } + } + } + + pub fn close(context: &mut UpdateContext<'_, 'gc>, handle: NetConnectionHandle) { + let Some(connection) = context.net_connections.connections.remove(handle) else { + return; + }; + + match connection.object { + NetConnectionObject::Avm2(object) => { + let mut activation = Avm2Activation::from_nothing(context.reborrow()); + let event = Avm2EventObject::net_status_event( + &mut activation, + "netStatus", + vec![ + ("code", "NetConnection.Connect.Closed"), + ("level", "status"), + ], + ); + Avm2::dispatch_event(&mut activation.context, event, object.into()); + } + } + } +} + +#[derive(Collect)] +#[collect(no_drop)] +pub struct NetConnection<'gc> { + object: NetConnectionObject<'gc>, +} diff --git a/core/src/player.rs b/core/src/player.rs index d285e99f6..4383b9187 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -39,6 +39,7 @@ use crate::library::Library; use crate::limits::ExecutionLimit; use crate::loader::{LoadBehavior, LoadManager}; use crate::locale::get_current_date_time; +use crate::net_connection::NetConnections; use crate::prelude::*; use crate::socket::Sockets; use crate::streams::StreamManager; @@ -167,6 +168,9 @@ struct GcRootData<'gc> { sockets: Sockets<'gc>, + /// List of active NetConnection objects. + net_connections: NetConnections<'gc>, + /// Dynamic root for allowing handles to GC objects to exist outside of the GC. dynamic_root: DynamicRootSet<'gc>, } @@ -195,6 +199,7 @@ impl<'gc> GcRootData<'gc> { &mut AudioManager<'gc>, &mut StreamManager<'gc>, &mut Sockets<'gc>, + &mut NetConnections<'gc>, DynamicRootSet<'gc>, ) { ( @@ -215,6 +220,7 @@ impl<'gc> GcRootData<'gc> { &mut self.audio_manager, &mut self.stream_manager, &mut self.sockets, + &mut self.net_connections, self.dynamic_root, ) } @@ -1889,6 +1895,7 @@ impl Player { audio_manager, stream_manager, sockets, + net_connections, dynamic_root, ) = root_data.update_context_params(); @@ -1940,6 +1947,7 @@ impl Player { stub_tracker: &mut self.stub_tracker, stream_manager, sockets, + net_connections, dynamic_root, }; @@ -2423,6 +2431,7 @@ impl PlayerBuilder { unbound_text_fields: Vec::new(), stream_manager: StreamManager::new(), sockets: Sockets::empty(), + net_connections: NetConnections::default(), dynamic_root, }, ),