diff --git a/src/main/scala/li/cil/oc/client/Sound.scala b/src/main/scala/li/cil/oc/client/Sound.scala index f8cba56ae0..6d9d5f4bc5 100644 --- a/src/main/scala/li/cil/oc/client/Sound.scala +++ b/src/main/scala/li/cil/oc/client/Sound.scala @@ -17,10 +17,11 @@ import paulscode.sound.SoundSystemConfig import java.net.{MalformedURLException, URL, URLConnection, URLStreamHandler} import java.util.UUID import scala.collection.mutable +import scala.ref.WeakReference object Sound { - private val sources = mutable.Map.empty[TileEntity, PseudoLoopingStream] + private val sources = mutable.WeakHashMap.empty[TileEntity, PseudoLoopingStream] private val commandQueue = mutable.PriorityQueue.empty[Command] private var lastVolume = FMLClientHandler.instance.getClient.gameSettings.getSoundLevel(SoundCategory.BLOCKS) @@ -52,8 +53,12 @@ object Sound { if (commandQueue.nonEmpty) { commandQueue.synchronized { while (commandQueue.nonEmpty && commandQueue.head.when < System.currentTimeMillis()) { - try commandQueue.dequeue()() catch { - case t: Throwable => OpenComputers.log.warn("Error processing sound command.", t) + if (commandQueue.head.tileEntity.get.isEmpty) { + commandQueue.dequeue() + } else { + try commandQueue.dequeue()() catch { + case t: Throwable => OpenComputers.log.warn("Error processing sound command.", t) + } } } } @@ -61,25 +66,25 @@ object Sound { } def startLoop(tileEntity: TileEntity, name: String, volume: Float = 1f, delay: Long = 0) { - if (Settings.get.soundVolume > 0 && Minecraft.getMinecraft.theWorld != null) { + if (Settings.get.soundVolume > 0) { commandQueue.synchronized { - commandQueue += new StartCommand(System.currentTimeMillis() + delay, tileEntity, name, volume) + commandQueue += new StartCommand(System.currentTimeMillis() + delay, new WeakReference[TileEntity](tileEntity), name, volume) } } } def stopLoop(tileEntity: TileEntity) { - if (Settings.get.soundVolume > 0 && Minecraft.getMinecraft.theWorld != null) { + if (Settings.get.soundVolume > 0) { commandQueue.synchronized { - commandQueue += new StopCommand(tileEntity) + commandQueue += new StopCommand(new WeakReference[TileEntity](tileEntity)) } } } def updatePosition(tileEntity: TileEntity) { - if (Settings.get.soundVolume > 0 && Minecraft.getMinecraft.theWorld != null) { + if (Settings.get.soundVolume > 0) { commandQueue.synchronized { - commandQueue += new UpdatePositionCommand(tileEntity) + commandQueue += new UpdatePositionCommand(new WeakReference[TileEntity](tileEntity)) } } } @@ -107,31 +112,31 @@ object Sound { @SubscribeEvent(priority = EventPriority.LOWEST) def onWorldUnload(event: WorldEvent.Unload) { + commandQueue.synchronized(commandQueue.clear()) sources.synchronized(try sources.foreach(_._2.stop()) catch { case _: Throwable => // Ignore. }) - sources.synchronized(sources.clear()) - commandQueue.synchronized(commandQueue.clear()) + sources.clear() } - private abstract class Command(val when: Long, val tileEntity: TileEntity) extends Ordered[Command] { + private abstract class Command(val when: Long, val tileEntity: WeakReference[TileEntity]) extends Ordered[Command] { def apply(): Unit override def compare(that: Command) = (that.when - when).toInt } - private class StartCommand(when: Long, tileEntity: TileEntity, val name: String, val volume: Float) extends Command(when, tileEntity) { + private class StartCommand(when: Long, tileEntity: WeakReference[TileEntity], val name: String, val volume: Float) extends Command(when, tileEntity) { override def apply() { sources.synchronized { - sources.getOrElseUpdate(tileEntity, new PseudoLoopingStream(tileEntity, volume)).play(name) + sources.getOrElseUpdate(tileEntity.get.get, new PseudoLoopingStream(tileEntity, volume)).play(name) } } } - private class StopCommand(tileEntity: TileEntity) extends Command(System.currentTimeMillis() + 1, tileEntity) { + private class StopCommand(tileEntity: WeakReference[TileEntity]) extends Command(System.currentTimeMillis() + 1, tileEntity) { override def apply() { sources.synchronized { - sources.remove(tileEntity) match { + sources.remove(tileEntity.get.get) match { case Some(sound) => sound.stop() case _ => } @@ -140,15 +145,15 @@ object Sound { // Remove all other commands for this tile entity from the queue. This // is inefficient, but we generally don't expect the command queue to // be very long, so this should be OK. - commandQueue ++= commandQueue.dequeueAll.filter(_.tileEntity != tileEntity) + commandQueue ++= commandQueue.dequeueAll.filter(_.tileEntity.get.get == tileEntity.get.get) } } } - private class UpdatePositionCommand(tileEntity: TileEntity) extends Command(System.currentTimeMillis(), tileEntity) { + private class UpdatePositionCommand(tileEntity: WeakReference[TileEntity]) extends Command(System.currentTimeMillis(), tileEntity) { override def apply() { sources.synchronized { - sources.get(tileEntity) match { + sources.get(tileEntity.get.get) match { case Some(sound) => sound.updatePosition() case _ => } @@ -156,7 +161,7 @@ object Sound { } } - private class PseudoLoopingStream(val tileEntity: TileEntity, val volume: Float, val source: String = UUID.randomUUID.toString) { + private class PseudoLoopingStream(val tileEntity: WeakReference[TileEntity], val volume: Float, val source: String = UUID.randomUUID.toString) { var initialized = false def updateVolume() { @@ -164,7 +169,7 @@ object Sound { } def updatePosition() { - if (tileEntity != null) soundSystem.setPosition(source, tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord) + if (tileEntity.get.isDefined) soundSystem.setPosition(source, tileEntity.get.get.xCoord, tileEntity.get.get.yCoord, tileEntity.get.get.zCoord) else soundSystem.setPosition(source, 0, 0, 0) } @@ -174,7 +179,7 @@ object Sound { val resource = (sound.func_148720_g: SoundPoolEntry).getSoundPoolEntryLocation if (!initialized) { initialized = true - if (tileEntity != null) soundSystem.newSource(false, source, toUrl(resource), resource.toString, true, tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord, SoundSystemConfig.ATTENUATION_LINEAR, 16) + if (tileEntity.get.isDefined) soundSystem.newSource(false, source, toUrl(resource), resource.toString, true, tileEntity.get.get.xCoord, tileEntity.get.get.yCoord, tileEntity.get.get.zCoord, SoundSystemConfig.ATTENUATION_LINEAR, 16) else soundSystem.newSource(false, source, toUrl(resource), resource.toString, false, 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0) updateVolume() soundSystem.activate(source)