core: Close sockets and timers when replacing the root movie
Turns out that the comment from `set_root_movie` was right all along! `set_root_movie` should not be used when replacing the root movie, because it will leave out unclosed resources. This patch introduces `replace_root_movie`, which is dedicated to be used when replacing (instead of just setting) the root movie. The best solution would be to use RAII, e.g. destroy and recreate the whole Player instance, but this patch is a first step towards proper resource control.
This commit is contained in:
parent
3d04971ae4
commit
c260a45603
|
@ -518,11 +518,11 @@ impl<'gc> AudioManager<'gc> {
|
|||
audio.stop_all_sounds();
|
||||
}
|
||||
|
||||
pub fn is_sound_playing(&mut self, sound: SoundInstanceHandle) -> bool {
|
||||
pub fn is_sound_playing(&self, sound: SoundInstanceHandle) -> bool {
|
||||
self.sounds.iter().any(|other| other.instance == sound)
|
||||
}
|
||||
|
||||
pub fn is_sound_playing_with_handle(&mut self, sound: SoundHandle) -> bool {
|
||||
pub fn is_sound_playing_with_handle(&self, sound: SoundHandle) -> bool {
|
||||
self.sounds.iter().any(|other| other.sound == Some(sound))
|
||||
}
|
||||
|
||||
|
|
|
@ -321,11 +321,11 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
|||
self.audio_manager.stop_all_sounds(self.audio)
|
||||
}
|
||||
|
||||
pub fn is_sound_playing(&mut self, sound: SoundInstanceHandle) -> bool {
|
||||
pub fn is_sound_playing(&self, sound: SoundInstanceHandle) -> bool {
|
||||
self.audio_manager.is_sound_playing(sound)
|
||||
}
|
||||
|
||||
pub fn is_sound_playing_with_handle(&mut self, sound: SoundHandle) -> bool {
|
||||
pub fn is_sound_playing_with_handle(&self, sound: SoundHandle) -> bool {
|
||||
self.audio_manager.is_sound_playing_with_handle(sound)
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
|||
///
|
||||
/// This should only be called once, as it makes no attempt at removing
|
||||
/// previous stage contents. If you need to load a new root movie, you
|
||||
/// should destroy and recreate the player instance.
|
||||
/// should use `replace_root_movie`.
|
||||
pub fn set_root_movie(&mut self, movie: SwfMovie) {
|
||||
if !self.forced_frame_rate {
|
||||
*self.frame_rate = movie.frame_rate().into();
|
||||
|
@ -453,6 +453,17 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
|||
|
||||
self.audio.set_frame_rate(*self.frame_rate);
|
||||
}
|
||||
|
||||
pub fn replace_root_movie(&mut self, movie: SwfMovie) {
|
||||
// FIXME Use RAII here, e.g. destroy and recreate
|
||||
// the player instance instead of cleaning up.
|
||||
|
||||
// Clean up the stage before loading another root movie.
|
||||
self.sockets.close_all();
|
||||
self.timers.remove_all();
|
||||
|
||||
self.set_root_movie(movie);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
||||
|
|
|
@ -1019,7 +1019,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
let movie = SwfMovie::from_data(&body, url.to_string(), loader_url)?;
|
||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||
uc.set_root_movie(movie);
|
||||
uc.replace_root_movie(movie);
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -1102,7 +1102,7 @@ impl<'gc> Loader<'gc> {
|
|||
"loadBytes",
|
||||
"replacing root movie"
|
||||
);
|
||||
uc.set_root_movie(movie);
|
||||
uc.replace_root_movie(movie);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
|
@ -170,27 +170,37 @@ impl<'gc> Sockets<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn close_all(&mut self) {
|
||||
for (_, socket) in self.sockets.drain() {
|
||||
Self::close_internal(socket);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&mut self, handle: SocketHandle) {
|
||||
if let Some(Socket {
|
||||
if let Some(socket) = self.sockets.remove(handle) {
|
||||
Self::close_internal(socket);
|
||||
}
|
||||
}
|
||||
|
||||
fn close_internal(socket: Socket) {
|
||||
let Socket {
|
||||
sender,
|
||||
target,
|
||||
connected: _,
|
||||
}) = self.sockets.remove(handle)
|
||||
{
|
||||
drop(sender); // NOTE: By dropping the sender, the reading task will close automatically.
|
||||
} = socket;
|
||||
|
||||
// Clear the buffers if the connection was closed.
|
||||
match target {
|
||||
SocketKind::Avm1(target) => {
|
||||
let target =
|
||||
XmlSocket::cast(target.into()).expect("target should be XmlSocket");
|
||||
drop(sender); // NOTE: By dropping the sender, the reading task will close automatically.
|
||||
|
||||
target.read_buffer().clear();
|
||||
}
|
||||
SocketKind::Avm2(target) => {
|
||||
target.read_buffer().clear();
|
||||
target.write_buffer().clear();
|
||||
}
|
||||
// Clear the buffers if the connection was closed.
|
||||
match target {
|
||||
SocketKind::Avm1(target) => {
|
||||
let target = XmlSocket::cast(target.into()).expect("target should be XmlSocket");
|
||||
|
||||
target.read_buffer().clear();
|
||||
}
|
||||
SocketKind::Avm2(target) => {
|
||||
target.read_buffer().clear();
|
||||
target.write_buffer().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,6 +258,10 @@ impl<'gc> Timers<'gc> {
|
|||
len < old_len
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) {
|
||||
self.timers.clear()
|
||||
}
|
||||
|
||||
/// Changes the delay of a timer.
|
||||
pub fn set_delay(&mut self, id: i32, interval: i32) {
|
||||
// SANITY: Set a minimum interval so we don't spam too much.
|
||||
|
|
Loading…
Reference in New Issue