diff --git a/build.properties b/build.properties index 6dfb28d41..ea1d0ce90 100644 --- a/build.properties +++ b/build.properties @@ -1,5 +1,5 @@ minecraft.version=1.7.2 -forge.version=10.12.0.1034 +forge.version=10.12.0.1046 oc.version=1.2.3 ccl.version=1.0.0.62 fmp.version=1.0.0.250 diff --git a/src/main/resources/assets/opencomputers/sound/computer_running.ogg b/src/main/resources/assets/opencomputers/sound/computer_running.ogg new file mode 100644 index 000000000..c9404f909 Binary files /dev/null and b/src/main/resources/assets/opencomputers/sound/computer_running.ogg differ diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 710194a1e..07a9bbe36 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -42,6 +42,10 @@ opencomputers { # Render robots' names as a label above them when near them robotLabels: true + + # The volume multiplier applied to sounds from this mod like the computer + # running noise. Disable sounds by setting this to zero. + soundVolume: 1.0 } # Computer related settings, concerns server performance and security. diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 9104b9077..4e5839986 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -17,6 +17,7 @@ class Settings(config: Config) { val textAntiAlias = config.getBoolean("client.textAntiAlias") val pasteShortcut = config.getStringList("client.pasteShortcut").toSet val robotLabels = config.getBoolean("client.robotLabels") + val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 min 2 val rTreeDebugRenderer = false // *Not* to be configurable via config file. // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index d6885b33f..a99af771e 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -9,7 +9,7 @@ import li.cil.oc.client.renderer.block.BlockRenderer import li.cil.oc.client.renderer.item.UpgradeRenderer import li.cil.oc.client.renderer.tileentity._ import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer -import li.cil.oc.common.{Proxy => CommonProxy, Sound, tileentity} +import li.cil.oc.common.{Proxy => CommonProxy, tileentity} import li.cil.oc.{Items, Settings, OpenComputers} import net.minecraftforge.client.MinecraftForgeClient import net.minecraftforge.common.MinecraftForge diff --git a/src/main/scala/li/cil/oc/client/Sound.scala b/src/main/scala/li/cil/oc/client/Sound.scala new file mode 100644 index 000000000..52ceb4187 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/Sound.scala @@ -0,0 +1,118 @@ +package li.cil.oc.client + +import cpw.mods.fml.client.FMLClientHandler +import cpw.mods.fml.common.eventhandler.SubscribeEvent +import java.util.{TimerTask, Timer, UUID} +import li.cil.oc.common.tileentity +import li.cil.oc.Settings +import net.minecraft.client.Minecraft +import net.minecraft.tileentity.TileEntity +import net.minecraftforge.client.event.sound.SoundLoadEvent +import net.minecraftforge.event.world.{WorldEvent, ChunkEvent} +import paulscode.sound.SoundSystemConfig +import scala.collection.convert.WrapAsScala._ +import scala.collection.mutable + +object Sound { + val sources = mutable.Map.empty[TileEntity, (String, Float)] + + var lastVolume = FMLClientHandler.instance.getClient.gameSettings.soundVolume + + val volumeCheckTimer = new Timer("OpenComputers-VolumeUpdater", true) + volumeCheckTimer.scheduleAtFixedRate(new TimerTask { + override def run() { + val volume = FMLClientHandler.instance.getClient.gameSettings.soundVolume + if (volume != lastVolume) { + lastVolume = volume + val system = Minecraft.getMinecraft.sndManager.sndSystem + sources.synchronized { + for ((source, volume) <- sources.values) { + system.setVolume(source, lastVolume * volume * Settings.get.soundVolume) + } + } + } + } + }, 5000, 500) + + def startLoop(tileEntity: TileEntity, name: String, volume: Float = 1f) { + if (Settings.get.soundVolume > 0) { + val resourceName = s"${Settings.resourceDomain}:$name" + val manager = Minecraft.getMinecraft.getSoundHandler + val sound = manager.soundPoolSounds.getRandomSoundFromSoundPool(resourceName) + sources.synchronized { + val (source, _) = sources.getOrElseUpdate(tileEntity, { + val source = UUID.randomUUID.toString + manager.sndSystem.newStreamingSource(false, source, sound.getSoundUrl, sound.getSoundName, true, tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord, SoundSystemConfig.ATTENUATION_LINEAR, 16.0f) + manager.sndSystem.setVolume(source, lastVolume * volume * Settings.get.soundVolume) + (source, volume) + }) + manager.sndSystem.fadeOutIn(source, sound.getSoundUrl, sound.getSoundName, 50, 500) + manager.sndSystem.play(source) + } + } + } + + def updatePosition(tileEntity: TileEntity) { + sources.synchronized { + sources.get(tileEntity) match { + case Some((source, _)) => Minecraft.getMinecraft.sndManager.sndSystem. + setPosition(source, tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord) + case _ => + } + } + } + + def stopLoop(tileEntity: TileEntity) { + sources.synchronized { + sources.get(tileEntity) match { + case Some((source, _)) => Minecraft.getMinecraft.sndManager.sndSystem.fadeOut(source, null, 500) + case _ => + } + } + } + + @SubscribeEvent + def onSoundLoad(event: SoundLoadEvent) { + for (i <- 1 to 6) { + event.manager.soundPoolSounds.addSound(Settings.resourceDomain + s":floppy_access$i.ogg") + } + for (i <- 1 to 7) { + event.manager.soundPoolSounds.addSound(Settings.resourceDomain + s":hdd_access$i.ogg") + } + event.manager.soundPoolSounds.addSound(Settings.resourceDomain + ":floppy_insert.ogg") + event.manager.soundPoolSounds.addSound(Settings.resourceDomain + ":floppy_eject.ogg") + + event.manager.soundPoolSounds.addSound(Settings.resourceDomain + ":computer_running.ogg") + } + + @SubscribeEvent + def onChunkUnload(event: ChunkEvent.Unload) { + cleanup(event.getChunk.chunkTileEntityMap.values) + } + + @SubscribeEvent + def onWorldUnload(event: WorldEvent.Unload) { + cleanup(event.world.loadedTileEntityList) + } + + def cleanup[_](list: Iterable[_]) { + val system = Minecraft.getMinecraft.sndManager.sndSystem + sources.synchronized { + list.foreach { + case robot: tileentity.RobotProxy => sources.remove(robot.robot) match { + case Some((source, _)) => + system.stop(source) + system.removeSource(source) + case _ => + } + case tileEntity: TileEntity => sources.remove(tileEntity) match { + case Some((source, _)) => + system.stop(source) + system.removeSource(source) + case _ => + } + case _ => + } + } + } +} diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala index 1cb2199aa..414723b76 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala @@ -176,6 +176,21 @@ object RobotRenderer extends TileEntitySpecialRenderer { GL11.glPushMatrix() GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5) + // If the move started while we were rendering and we have a reference to + // the *old* proxy the robot would be rendered at the wrong position, so we + // correct for the offset. + if (robot.proxy != proxy) { + GL11.glTranslated(robot.proxy.x - proxy.x, robot.proxy.y - proxy.y, robot.proxy.z - proxy.z) + } + + if (robot.isAnimatingMove) { + val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble + val dx = robot.moveFromX - robot.x + val dy = robot.moveFromY - robot.y + val dz = robot.moveFromZ - robot.z + GL11.glTranslated(dx * remaining, dy * remaining, dz * remaining) + } + val name = robot.name if (Settings.get.robotLabels && !Strings.isNullOrEmpty(name) && x * x + y * y + z * z < RendererLivingEntity.NAME_TAG_RANGE) { GL11.glPushMatrix() @@ -218,21 +233,6 @@ object RobotRenderer extends TileEntitySpecialRenderer { GL11.glDisable(GL11.GL_BLEND) GL11.glColor4f(1, 1, 1, 1) - // If the move started while we were rendering and we have a reference to - // the *old* proxy the robot would be rendered at the wrong position, so we - // correct for the offset. - if (robot.proxy != proxy) { - GL11.glTranslated(robot.proxy.x - proxy.x, robot.proxy.y - proxy.y, robot.proxy.z - proxy.z) - } - - if (robot.isAnimatingMove) { - val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble - val dx = robot.moveFromX - robot.x - val dy = robot.moveFromY - robot.y - val dz = robot.moveFromZ - robot.z - GL11.glTranslated(dx * remaining, dy * remaining, dz * remaining) - } - if (robot.isAnimatingTurn) { val remaining = (robot.animationTicksLeft - f) / robot.animationTicksTotal.toDouble GL11.glRotated(90 * remaining, 0, robot.turnAxis, 0) diff --git a/src/main/scala/li/cil/oc/common/Sound.scala b/src/main/scala/li/cil/oc/common/Sound.scala index dcc360259..6d2bd6818 100644 --- a/src/main/scala/li/cil/oc/common/Sound.scala +++ b/src/main/scala/li/cil/oc/common/Sound.scala @@ -33,5 +33,4 @@ object Sound { lastPlayed += t -> (System.currentTimeMillis() + 500) } } - } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/Computer.scala index f265b7e7b..f99b4c455 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Computer.scala @@ -2,11 +2,12 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.common.Optional import cpw.mods.fml.relauncher.{Side, SideOnly} -import li.cil.oc.Settings import li.cil.oc.api.Machine import li.cil.oc.api.machine.Owner import li.cil.oc.api.network._ +import li.cil.oc.client.Sound import li.cil.oc.server.{PacketSender => ServerPacketSender, driver} +import li.cil.oc.Settings import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.{NBTTagString, NBTTagCompound} @@ -51,6 +52,8 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv def setRunning(value: Boolean) = { _isRunning = value world.markBlockForUpdate(x, y, z) + if (_isRunning) Sound.startLoop(this, "computer_running", 0.5f) + else Sound.stopLoop(this) this } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index a37507358..3cc8bc75c 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -6,6 +6,7 @@ import cpw.mods.fml.common.Optional.Method import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.api.Network import li.cil.oc.api.network.{Connector, Visibility, Node} +import li.cil.oc.client.Sound import li.cil.oc.common import li.cil.oc.server.{PacketSender => ServerPacketSender, driver, component} import li.cil.oc.util.ExtendedNBT._ @@ -63,6 +64,8 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun def setRunning(number: Int, value: Boolean) = { _isRunning(number) = value world.markBlockForUpdate(x, y, z) + if (anyRunning) Sound.startLoop(this, "computer_running", 1.5f) + else Sound.stopLoop(this) this } @@ -289,6 +292,8 @@ class Rack extends Hub with PowerBalancer with Inventory with Rotatable with Bun nbt.getTagList("terminals", NBT.TAG_COMPOUND). foreach((list, index) => if (index < terminals.length) terminals(index).readFromNBTForClient(list.getCompoundTagAt(index))) range = nbt.getInteger("range") + if (anyRunning) Sound.startLoop(this, "computer_running", 1.5f) + else Sound.stopLoop(this) } override def writeToNBTForClient(nbt: NBTTagCompound) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index d799eb3f6..379944443 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -3,6 +3,7 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{SideOnly, Side} import li.cil.oc.api.driver.Slot import li.cil.oc.api.network._ +import li.cil.oc.common import li.cil.oc.common.block.Delegator import li.cil.oc.server.component.GraphicsCard import li.cil.oc.server.component.robot @@ -21,7 +22,6 @@ import net.minecraftforge.common.util.ForgeDirection import scala.io.Source import scala.Some import java.util.logging.Level -import li.cil.oc.common.Sound // Implementation note: this tile entity is never directly added to the world. // It is always wrapped by a `RobotProxy` tile entity, which forwards any @@ -325,6 +325,9 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w ServerPacketSender.sendRobotXp(this) } } + else if (isRunning && isAnimatingMove) { + client.Sound.updatePosition(this) + } } override def validate() { @@ -502,7 +505,7 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w ServerPacketSender.sendRobotEquippedUpgradeChange(this, getStackInSlot(slot)) } if (isFloppySlot(slot)) { - Sound.playDiskInsert(this) + common.Sound.playDiskInsert(this) } if (isComponentSlot(slot)) { super.onItemAdded(slot, stack) @@ -524,7 +527,7 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w ServerPacketSender.sendRobotEquippedUpgradeChange(this, null) } if (isFloppySlot(slot)) { - Sound.playDiskEject(this) + common.Sound.playDiskEject(this) } if (isInventorySlot(slot)) { computer.signal("inventory_changed", Int.box(slot - actualSlot(0) + 1)) diff --git a/src/main/scala/li/cil/oc/server/network/Network.scala b/src/main/scala/li/cil/oc/server/network/Network.scala index c551f1490..29640a7f1 100644 --- a/src/main/scala/li/cil/oc/server/network/Network.scala +++ b/src/main/scala/li/cil/oc/server/network/Network.scala @@ -180,13 +180,13 @@ private class Network private(private val data: mutable.Map[String, Network.Vert // ----------------------------------------------------------------------- // - private def contains(node: ImmutableNode) = data.contains(node.address) + private def contains(node: MutableNode) = node.network == wrapper && data.contains(node.address) private def node(node: ImmutableNode) = data(node.address) private def addNew(node: MutableNode) = { val newNode = new Network.Vertex(node) - if (node.address == null) + if (node.address == null || data.contains(node.address)) node.address = java.util.UUID.randomUUID().toString data += node.address -> newNode node match {