From 66097fdd6cf1ac950555473304336247d304ce69 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Wed, 22 Apr 2026 22:30:37 +0200 Subject: [PATCH] fixes to node ticking and beeping --- .../org/neoflock/neocomputers/NeoComputers.kt | 24 +++ .../neoflock/neocomputers/block/CaseBlock.kt | 2 +- .../neoflock/neocomputers/block/NodeBlock.kt | 48 +++++- .../neocomputers/entity/CaseBlockEntity.kt | 37 +++-- .../neocomputers/entity/MachineEntity.kt | 7 +- .../neocomputers/gui/screen/CaseScreen.kt | 2 + .../neocomputers/item/DataComponents.kt | 3 + .../neoflock/neocomputers/item/TunnelCard.kt | 4 +- .../neocomputers/network/Networking.kt | 4 +- .../neoflock/neocomputers/sounds/Sounds.kt | 151 ++++++++++++++++++ .../assets/neocomputers/lang/en_us.json | 2 + 11 files changed, 258 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt index 2ef8caf..b25d664 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt @@ -1,6 +1,7 @@ package org.neoflock.neocomputers import dev.architectury.event.events.client.ClientLifecycleEvent +import dev.architectury.event.events.common.LifecycleEvent import dev.architectury.event.events.common.PlayerEvent import dev.architectury.event.events.common.TickEvent import dev.architectury.networking.NetworkManager @@ -64,6 +65,22 @@ object NeoComputers { NodeSynchronizer.syncScreens() } + TickEvent.PLAYER_POST.register { + Sounds.tickCustomSounds() + } + + LifecycleEvent.SERVER_STARTING.register { + Networking.allNodes.remove() + Networking.wirelessNodes.remove() + Networking.channels.remove() + } + + ClientLifecycleEvent.CLIENT_STARTED.register { + Networking.allNodes.remove() + Networking.wirelessNodes.remove() + Networking.channels.remove() + } + PlayerEvent.CLOSE_MENU.register { player, menu -> if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player) @@ -105,11 +122,18 @@ object NeoComputers { scr.processScreenStatePacket(packet.buffer) } }) + + NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC, { + packet, ctx -> + // TODO: implement volume + Sounds.beep(packet.pos.center, packet.pattern, packet.freq, packet.duration.toMillis().toInt()) + }) }} EnvExecutor.runInEnv(Env.SERVER) {{ // https://github.com/architectury/architectury-api/issues/518 NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC) NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC) + NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC) }} LOGGER.info("Registered!") diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt index ff04f79..1b64059 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt @@ -37,7 +37,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel( } } - override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState) + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState).initNetworking() override fun createBlockStateDefinition(builder: StateDefinition.Builder) { builder.add(COMPUTER_RUNNING) diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt index 0cfa79c..d27eddb 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt @@ -25,6 +25,7 @@ import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.PowerRole +import java.time.Duration object NodeSynchronizer { class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload { @@ -71,8 +72,8 @@ object NodeSynchronizer { class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload { companion object { - val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data") - val TYPE = CustomPacketPayload.Type(SCREEN_SYNC_ID) + val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data") + val TYPE = CustomPacketPayload.Type(SCREEN_DATA_ID) val CODEC = object : StreamCodec { override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload { val id = buf.readByteArray().decodeToString() @@ -90,6 +91,33 @@ object NodeSynchronizer { override fun type() = TYPE } + class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload { + companion object { + val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data") + val TYPE = CustomPacketPayload.Type(BEEP_DATA_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload { + val pos = buf.readBlockPos() + val pattern = buf.readUtf() + val freq = buf.readVarInt() + val duration = buf.readVarLong() + val volume = buf.readDouble() + return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) { + buf.writeBlockPos(payload.pos) + buf.writeUtf(payload.pattern) + buf.writeVarInt(payload.freq) + buf.writeVarLong(payload.duration.toMillis()) + buf.writeDouble(payload.volume) + } + } + } + + override fun type() = TYPE + } + val screenMap = mutableMapOf() fun playerScreenClosed(player: ServerPlayer) { @@ -113,6 +141,14 @@ object NodeSynchronizer { fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) { NetworkManager.sendToServer(ScreenDataPayload("", friendlyByteBuf)) } + + fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) { + if(level is ServerLevel) { + level.players().forEach { + NetworkManager.sendToPlayer(it, beepDataPayload) + } + } + } } abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) { @@ -177,7 +213,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl stateIsDirty = true } - fun needsSynchronization() = stateIsDirty + open fun needsSynchronization() = stateIsDirty open fun tickNode(level: Level) { if(!level.isClientSide) { @@ -203,9 +239,6 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl override fun setRemoved() { super.setRemoved() - if(node.powerRole == PowerRole.GENERATOR) { - NeoComputers.LOGGER.info("removed generator ${node.address}") - } Networking.removeNode(node) } @@ -229,7 +262,8 @@ abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(pr ): BlockEntityTicker? { return object : BlockEntityTicker { override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) { - if(blockEntity !is NodeBlockEntity) return; + if(blockEntity !is NodeBlockEntity) return + if(Networking.getNode(blockEntity.node.address) == null) blockEntity.initNetworking() blockEntity.tickNode(level) } } diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt index aebb6a4..6c08cd3 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt @@ -1,10 +1,7 @@ package org.neoflock.neocomputers.entity import net.minecraft.client.Minecraft -import net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler -import net.minecraft.client.resources.sounds.EntityBoundSoundInstance import net.minecraft.client.resources.sounds.SoundInstance -import net.minecraft.client.sounds.LoopingAudioStream import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.core.HolderLookup @@ -14,7 +11,6 @@ import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerPlayer import net.minecraft.sounds.SoundSource -import net.minecraft.world.Container import net.minecraft.world.ContainerHelper import net.minecraft.world.MenuProvider import net.minecraft.world.entity.player.Inventory @@ -25,6 +21,7 @@ import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.block.CaseBlock import org.neoflock.neocomputers.block.NodeBlockEntity +import org.neoflock.neocomputers.block.NodeSynchronizer import org.neoflock.neocomputers.block.dirToIdx import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.item.ComponentItem @@ -45,7 +42,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti var soundInstance: SoundInstance? = null override val node = object : Networking.Node() { - override var powerRole = PowerRole.STORAGE + override var powerRole = PowerRole.CONSUMER override var energyCapacity: Long = 500 } @@ -130,7 +127,6 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti isOn = value val world = level ?: return blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn) - if(value) beepAsync(8000, Duration.ofSeconds(1), 1.0) if(world.isClientSide) { if(value) { soundInstance = ComputerRunningSoundInstance(this, Sounds.COMPUTER_RUNNING.get(), SoundSource.AMBIENT) @@ -147,29 +143,50 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti } override fun start(): Boolean { + if(isOn) return true err = null + val archs = getMachineArchitectures() + // Beep patterns taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/server/machine/Machine.scala + if(archs.isEmpty()) { + crash("no cpu") + beepAsync("-..") + return false + } if(getMachineComponentsUsed() > getMachineComponentsTotal()) { crash("too many components") + beepAsync("-..") return false } if(node.energy < 100) { crash("not enough energy") + // we add a beep for the special case where we do have a little bit of energy :P + if(node.energy > 0) beepAsync("..") return false } if(getMachineMemoryTotal() == 0L) { crash("no memory provided") + beepAsync("-.") return false } + if(arch !in archs) { + // Just pick one! + arch = archs.first() + } + beepAsync(".") setRunning(true) return isOn } override fun stop(): Boolean { + if(!isOn) return false setRunning(false) return isOn } override fun crash(error: String): Boolean { + if(isOn) { + beepAsync("--") + } setRunning(false) err = error return true @@ -190,8 +207,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti return old } - override fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean { - NeoComputers.LOGGER.warn("beep not yet implemented") + override fun beepAsync(pattern: String, frequency: Int, duration: Duration, volume: Double): Boolean { + NodeSynchronizer.emitBeep(level!!, NodeSynchronizer.BeepDataPayload(getMachineBlockPosition(), pattern, frequency, duration, volume)) return true } @@ -231,10 +248,6 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti override fun getDisplayName(): Component? = Component.literal("Computer") override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this) - override fun setChanged() { - super.setChanged() - } - override fun setRemoved() { setRunning(false) super.setRemoved() diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/MachineEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/MachineEntity.kt index ef7ccb9..27acd6d 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/MachineEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/MachineEntity.kt @@ -19,7 +19,12 @@ interface MachineEntity { fun getMachineBlockPosition(): BlockPos fun getMachineLevel(): Level - fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean + // Pattern can have dots (.), dashes (-) and spaces ( ). + // Each character is duration long, and has a 50ms break. + // For non-short ones, which are typically reserved only for hardware interactions, + // the duration is doubled. + // Architectures should only use short ones. + fun beepAsync(pattern: String, frequency: Int = 1000, duration: Duration = Duration.ofMillis(200), volume: Double = 1.0): Boolean fun isRunning(): Boolean fun start(): Boolean diff --git a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt index 7f33d15..f608c14 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt @@ -9,11 +9,13 @@ import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation import net.minecraft.world.entity.player.Inventory import net.minecraft.world.inventory.tooltip.TooltipComponent +import net.minecraft.world.phys.Vec3 import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.block.NodeSynchronizer import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.gui.widget.ButtonSprites import org.neoflock.neocomputers.gui.widget.ImagerButton +import org.neoflock.neocomputers.sounds.Sounds import org.neoflock.neocomputers.utils.Formatting import org.neoflock.neocomputers.utils.GenericContainerScreen import java.util.Optional diff --git a/src/main/kotlin/org/neoflock/neocomputers/item/DataComponents.kt b/src/main/kotlin/org/neoflock/neocomputers/item/DataComponents.kt index 1977f54..ff3ac77 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/item/DataComponents.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/item/DataComponents.kt @@ -23,4 +23,7 @@ object DataComponents { DataComponentType.builder().persistent(Codec.INT).build()) val EEPROM_DATASIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_datasize"), DataComponentType.builder().persistent(Codec.INT).build()) + + val TUNNEL_CHANNEL = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "tunnel_channel"), + DataComponentType.builder().persistent(Codec.STRING).build()) } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt b/src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt index 2b07461..872c99a 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt @@ -7,9 +7,8 @@ import net.minecraft.world.item.TooltipFlag import org.neoflock.neocomputers.entity.MachineEntity import org.neoflock.neocomputers.gui.widget.ComponentRoles import org.neoflock.neocomputers.network.Networking -import org.neoflock.neocomputers.utils.Formatting -class TunnelCard: Item(Properties()), ComponentItem { +class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "creative")), ComponentItem { // yes, we're counting TUNNEL as a conventional networking card override fun getComponentRoles(itemStack: ItemStack): Set = setOf(ComponentRoles.CARD, ComponentRoles.NETWORK) @@ -33,6 +32,7 @@ class TunnelCard: Item(Properties()), ComponentItem { val addr = itemStack.get(DataComponents.ADDRESS) val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr) list.addLast(addrComp) + list.addLast(Component.translatable("neocomputers.tunnel.channel", itemStack.get(DataComponents.TUNNEL_CHANNEL) ?: "creative")) // TODO: show max packet size and whatnot } super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag) diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt index 97eea46..867b278 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt @@ -288,7 +288,7 @@ object Networking { // TODO: use setter, more convenient fun changeNodeAddress(node: Node, address: UUID) { if(node.address.equals(address)) return - NeoComputers.LOGGER.info("remapping node from ${node.address} to $address") + if(node.address !in allNodes.get()) return allNodes.get().remove(node.address) node.address = address allNodes.get()[address] = node @@ -296,7 +296,6 @@ object Networking { fun addNode(node: Node) { if(node.address in allNodes.get()) return - NeoComputers.LOGGER.info("adding node ${node.address}") allNodes.get()[node.address] = node if(node is WirelessEndpoint) { wirelessNodes.get().add(node); @@ -311,7 +310,6 @@ object Networking { fun removeNode(node: Node) { if(node.address !in allNodes.get()) return - NeoComputers.LOGGER.info("removing node ${node.address}") allNodes.get().forEach { it.value.onNodeRemoved(node) } // toList() in order to copy it node.connections.toList().forEach { diff --git a/src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt b/src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt index 9cfedb5..99b8f4f 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt @@ -1,10 +1,20 @@ package org.neoflock.neocomputers.sounds import dev.architectury.registry.registries.DeferredRegister +import net.minecraft.client.Minecraft import net.minecraft.core.registries.Registries import net.minecraft.resources.ResourceLocation import net.minecraft.sounds.SoundEvent +import net.minecraft.world.phys.Vec3 +import org.lwjgl.BufferUtils import org.neoflock.neocomputers.NeoComputers +import org.lwjgl.openal.AL10 +import java.nio.ByteBuffer +import kotlin.experimental.xor +import kotlin.math.PI +import kotlin.math.max +import kotlin.math.sign +import kotlin.math.sin object Sounds { val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!! @@ -14,4 +24,145 @@ object Sounds { fun registerSound(name: String) = SOUNDS.register(name) { SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, name)) }!! + + val BEEP_SAMPLERATE = 44100 + val BEEP_AMPLITUDE = 32f + val BEEP_MAXDIST = 16f + + // Also largely taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala + + val allSounds = ThreadLocal.withInitial { mutableListOf() } + + class CustomSoundBuffer { + var dead: Boolean = true + var buffer: Int = -1 + var source: Int = -1 + + fun start(x: Float, y: Float, z: Float, data: ByteBuffer, gain: Float): Int? { + // clear errors or smth idk + AL10.alGetError() + + // written in a C style by a C dev + // all this work on a JVM project and I'm still writing C + // would be better if Kotlin had goto btw just saying + val ok = AL10.AL_NO_ERROR + var err = ok + buffer = AL10.alGenBuffers() + err = AL10.alGetError() + if(err != ok) return err + + AL10.alBufferData(buffer, AL10.AL_FORMAT_MONO8, data, BEEP_SAMPLERATE) + err = AL10.alGetError() + if(err != ok) { + AL10.alDeleteBuffers(buffer) + return err + } + + source = AL10.alGenSources() + err = AL10.alGetError() + if(err != ok) { + AL10.alDeleteBuffers(buffer) + return err + } + + AL10.alSourceQueueBuffers(source, buffer) + err = AL10.alGetError() + if(err != ok) { + AL10.alDeleteBuffers(buffer) + AL10.alDeleteSources(source) + return err + } + + AL10.alSource3f(source, AL10.AL_POSITION, x, y, z) + AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, BEEP_MAXDIST) + AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, BEEP_MAXDIST) + AL10.alSourcef(source, AL10.AL_GAIN, gain * 0.3f) + err = AL10.alGetError() + if(err != ok) { + AL10.alDeleteBuffers(buffer) + AL10.alDeleteSources(source) + return err + } + + AL10.alSourcePlay(source) + err = AL10.alGetError() + if(err != ok) { + AL10.alDeleteBuffers(buffer) + AL10.alDeleteSources(source) + return err + } + + dead = false + return null + } + + fun checkDone(): Boolean { + if(dead) return true + if(AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) return false + NeoComputers.LOGGER.info("sound buffer stopped") + dead = true + AL10.alDeleteSources(source) + AL10.alDeleteBuffers(buffer) + return true + } + } + + fun beep(pos: Vec3, pattern: String, frequency: Int = 1000, duration: Int = 200) { + NeoComputers.LOGGER.info("Beep: $pattern, $frequency Hz, $duration ms") + val mc = Minecraft.getInstance() + val playerPos = mc.player?.position() ?: pos + val distanceBasedGain = max(0.0, 1 - pos.distanceTo(playerPos) / BEEP_MAXDIST).toFloat() + val volume = 1.0 + val gain = distanceBasedGain * volume + if (gain <= 0 || BEEP_AMPLITUDE <= 0) return + + // Algorithm effectively ported over from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala + // We do add support for spaces tho + val charArr = pattern.toCharArray() + val sampleCounts = charArr.map { if(it == '.') duration else 2 * duration }.map { it * BEEP_SAMPLERATE / 1000 } + val pauseSample = 50 * BEEP_SAMPLERATE / 1000 + + val finalBuf = BufferUtils.createByteBuffer(sampleCounts.sum() + pauseSample * sampleCounts.lastIndex) + val step = frequency.toFloat() / BEEP_SAMPLERATE + var off = 0f + for((i, sampleCount) in sampleCounts.withIndex()) { + if(charArr[i] == ' ') { + for(sample in 0.. 1) off -= 1f + finalBuf.put(value) + } + } + if(finalBuf.hasRemaining()) { + for(sample in 0..() + while(finalBuf.hasRemaining()) l.addLast(finalBuf.get().toInt()) + finalBuf.rewind() + NeoComputers.LOGGER.info("$l") + + val sound = CustomSoundBuffer() + val soundErr = sound.start(pos.x.toFloat(), pos.y.toFloat(), pos.z.toFloat(), finalBuf, gain.toFloat()) + if(soundErr != null) { + NeoComputers.LOGGER.error("Playing beep failed, OpenAL exit code of $soundErr") + return + } + + NeoComputers.LOGGER.info("Beeping with ${finalBuf.capacity()} samples") + allSounds.get().addLast(sound) + } + + fun tickCustomSounds() { + allSounds.get().removeIf { it.checkDone() } + } } \ No newline at end of file diff --git a/src/main/resources/assets/neocomputers/lang/en_us.json b/src/main/resources/assets/neocomputers/lang/en_us.json index 10246cb..294c0ff 100644 --- a/src/main/resources/assets/neocomputers/lang/en_us.json +++ b/src/main/resources/assets/neocomputers/lang/en_us.json @@ -28,6 +28,7 @@ "item.neocomputers.lan": "Wired Network Card", "item.neocomputers.wlan0": "Wireless Network Card (Tier 1)", "item.neocomputers.wlan1": "Wireless Network Card (Tier 2)", + "item.neocomputers.tunnel": "Linked Card", "item.neocomputers.data0": "Data Card (Tier 1)", "item.neocomputers.data1": "Data Card (Tier 2)", "item.neocomputers.data2": "Data Card (Tier 3)", @@ -47,5 +48,6 @@ "neocomputers.memory.capacity": "Capacity: %1$s", "neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s", "neocomputers.eeprom.dataused": "Data Storage: %1$s / %2$s", + "neocomputers.tunnel.channel": "Linked Channel: %1$s", "sounds.neocomputers.computer_running": "Computer Fans" } \ No newline at end of file