fixes to node ticking and beeping
This commit is contained in:
@@ -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!")
|
||||
|
||||
@@ -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<Block?, BlockState?>) {
|
||||
builder.add(COMPUTER_RUNNING)
|
||||
|
||||
@@ -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<ScreenDataPayload>(SCREEN_SYNC_ID)
|
||||
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
|
||||
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<BeepDataPayload>(BEEP_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
|
||||
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<ServerPlayer, NodeBlockEntity>()
|
||||
|
||||
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<T>? {
|
||||
return object : BlockEntityTicker<T> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,4 +23,7 @@ object DataComponents {
|
||||
DataComponentType.builder<Int>().persistent(Codec.INT).build())
|
||||
val EEPROM_DATASIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_datasize"),
|
||||
DataComponentType.builder<Int>().persistent(Codec.INT).build())
|
||||
|
||||
val TUNNEL_CHANNEL = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "tunnel_channel"),
|
||||
DataComponentType.builder<String>().persistent(Codec.STRING).build())
|
||||
}
|
||||
@@ -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<String> = 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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<CustomSoundBuffer>() }
|
||||
|
||||
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..<sampleCount) {
|
||||
finalBuf.put(127)
|
||||
}
|
||||
} else {
|
||||
for(sample in 0..<sampleCount) {
|
||||
val angle = 2 * PI * off
|
||||
val value = (sin(angle).sign * BEEP_AMPLITUDE).toInt().toByte().xor(0x80.toByte())
|
||||
off += step
|
||||
if(off > 1) off -= 1f
|
||||
finalBuf.put(value)
|
||||
}
|
||||
}
|
||||
if(finalBuf.hasRemaining()) {
|
||||
for(sample in 0..<pauseSample) {
|
||||
finalBuf.put(127)
|
||||
}
|
||||
}
|
||||
}
|
||||
finalBuf.rewind()
|
||||
val l = mutableListOf<Int>()
|
||||
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() }
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user