Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers
This commit is contained in:
@@ -3,9 +3,11 @@ package org.neoflock.neocomputers
|
|||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
import dev.architectury.event.events.client.ClientLifecycleEvent
|
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.PlayerEvent
|
||||||
import dev.architectury.event.events.common.TickEvent
|
import dev.architectury.event.events.common.TickEvent
|
||||||
import dev.architectury.networking.NetworkManager
|
import dev.architectury.networking.NetworkManager
|
||||||
|
import dev.architectury.platform.Platform
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import org.neoflock.neocomputers.block.Blocks
|
import org.neoflock.neocomputers.block.Blocks
|
||||||
import org.neoflock.neocomputers.entity.BlockEntities
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
@@ -84,6 +86,22 @@ object NeoComputers {
|
|||||||
NodeSynchronizer.syncScreens()
|
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 {
|
PlayerEvent.CLOSE_MENU.register {
|
||||||
player, menu ->
|
player, menu ->
|
||||||
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
|
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
|
||||||
@@ -94,6 +112,10 @@ object NeoComputers {
|
|||||||
NodeSynchronizer.playerScreenClosed(player)
|
NodeSynchronizer.playerScreenClosed(player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// networking has no way to define a C2S packet type, so we need the listener on both
|
||||||
|
// however, defining it separately on both breaks both ends
|
||||||
|
// so we define it once, but on both platforms
|
||||||
|
if(Platform.getEnvironment() == Env.CLIENT || Platform.getEnvironment() == Env.SERVER) {
|
||||||
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
|
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
|
||||||
packet, ctx ->
|
packet, ctx ->
|
||||||
val player = ctx.player
|
val player = ctx.player
|
||||||
@@ -101,6 +123,7 @@ object NeoComputers {
|
|||||||
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
|
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// we have to do this because the datagen task runs in the physical server
|
// we have to do this because the datagen task runs in the physical server
|
||||||
EnvExecutor.runInEnv(Env.CLIENT) {{
|
EnvExecutor.runInEnv(Env.CLIENT) {{
|
||||||
@@ -120,11 +143,18 @@ object NeoComputers {
|
|||||||
scr.processScreenStatePacket(packet.buffer)
|
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) {{
|
EnvExecutor.runInEnv(Env.SERVER) {{
|
||||||
// https://github.com/architectury/architectury-api/issues/518
|
// https://github.com/architectury/architectury-api/issues/518
|
||||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC)
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC)
|
||||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
|
||||||
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
LOGGER.info("Registered!")
|
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?>) {
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||||
builder.add(COMPUTER_RUNNING)
|
builder.add(COMPUTER_RUNNING)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import net.minecraft.world.level.block.entity.BlockEntityType
|
|||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import org.neoflock.neocomputers.NeoComputers
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
import org.neoflock.neocomputers.network.Networking
|
import org.neoflock.neocomputers.network.Networking
|
||||||
|
import org.neoflock.neocomputers.network.PowerRole
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
object NodeSynchronizer {
|
object NodeSynchronizer {
|
||||||
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||||
@@ -70,8 +72,8 @@ object NodeSynchronizer {
|
|||||||
|
|
||||||
class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||||
companion object {
|
companion object {
|
||||||
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
||||||
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_SYNC_ID)
|
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_ID)
|
||||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
|
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
|
||||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
|
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
|
||||||
val id = buf.readByteArray().decodeToString()
|
val id = buf.readByteArray().decodeToString()
|
||||||
@@ -89,6 +91,33 @@ object NodeSynchronizer {
|
|||||||
override fun type() = TYPE
|
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>()
|
val screenMap = mutableMapOf<ServerPlayer, NodeBlockEntity>()
|
||||||
|
|
||||||
fun playerScreenClosed(player: ServerPlayer) {
|
fun playerScreenClosed(player: ServerPlayer) {
|
||||||
@@ -112,6 +141,14 @@ object NodeSynchronizer {
|
|||||||
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
|
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
|
||||||
NetworkManager.sendToServer(ScreenDataPayload("", 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) {
|
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
|
||||||
@@ -134,7 +171,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
|||||||
|
|
||||||
// runs on the client, meant to decode server state packets to synchronize client state
|
// runs on the client, meant to decode server state packets to synchronize client state
|
||||||
open fun syncWithUpstream(packet: FriendlyByteBuf) {
|
open fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||||
node.address = packet.readUUID()
|
Networking.changeNodeAddress(node, packet.readUUID())
|
||||||
node.energy = packet.readLong()
|
node.energy = packet.readLong()
|
||||||
node.energyCapacity = packet.readLong()
|
node.energyCapacity = packet.readLong()
|
||||||
node.reachability = packet.readEnum(node.reachability.javaClass)
|
node.reachability = packet.readEnum(node.reachability.javaClass)
|
||||||
@@ -176,7 +213,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
|||||||
stateIsDirty = true
|
stateIsDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun needsSynchronization() = stateIsDirty
|
open fun needsSynchronization() = stateIsDirty
|
||||||
|
|
||||||
open fun tickNode(level: Level) {
|
open fun tickNode(level: Level) {
|
||||||
if(!level.isClientSide) {
|
if(!level.isClientSide) {
|
||||||
@@ -225,7 +262,8 @@ abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(pr
|
|||||||
): BlockEntityTicker<T>? {
|
): BlockEntityTicker<T>? {
|
||||||
return object : BlockEntityTicker<T> {
|
return object : BlockEntityTicker<T> {
|
||||||
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: 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)
|
blockEntity.tickNode(level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,5 +87,6 @@ object BlockEntities {
|
|||||||
PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get())
|
PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get())
|
||||||
PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get())
|
PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get())
|
||||||
PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get())
|
PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get())
|
||||||
|
PowerManager.registerPowerBlockEntity(CASE_ENTITY.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package org.neoflock.neocomputers.entity
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft
|
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.resources.sounds.SoundInstance
|
||||||
import net.minecraft.client.sounds.LoopingAudioStream
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
@@ -14,7 +11,6 @@ import net.minecraft.network.FriendlyByteBuf
|
|||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
import net.minecraft.sounds.SoundSource
|
import net.minecraft.sounds.SoundSource
|
||||||
import net.minecraft.world.Container
|
|
||||||
import net.minecraft.world.ContainerHelper
|
import net.minecraft.world.ContainerHelper
|
||||||
import net.minecraft.world.MenuProvider
|
import net.minecraft.world.MenuProvider
|
||||||
import net.minecraft.world.entity.player.Inventory
|
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.NeoComputers
|
||||||
import org.neoflock.neocomputers.block.CaseBlock
|
import org.neoflock.neocomputers.block.CaseBlock
|
||||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||||
|
import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||||
import org.neoflock.neocomputers.block.dirToIdx
|
import org.neoflock.neocomputers.block.dirToIdx
|
||||||
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||||
import org.neoflock.neocomputers.item.ComponentItem
|
import org.neoflock.neocomputers.item.ComponentItem
|
||||||
@@ -40,10 +37,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
|||||||
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
|
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
|
||||||
|
|
||||||
var isOn = false
|
var isOn = false
|
||||||
|
var err: String? = null
|
||||||
|
var arch = "Lua 5.3"
|
||||||
var soundInstance: SoundInstance? = null
|
var soundInstance: SoundInstance? = null
|
||||||
|
|
||||||
override val node = object : Networking.Node() {
|
override val node = object : Networking.Node() {
|
||||||
override var powerRole = PowerRole.STORAGE
|
override var powerRole = PowerRole.CONSUMER
|
||||||
override var energyCapacity: Long = 500
|
override var energyCapacity: Long = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +69,14 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
|||||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||||
super.encodeScreenData(player, packet)
|
super.encodeScreenData(player, packet)
|
||||||
packet.writeBoolean(isOn)
|
packet.writeBoolean(isOn)
|
||||||
|
packet.writeByteArray((err ?: "").encodeToByteArray())
|
||||||
|
packet.writeLong(node.energy)
|
||||||
|
packet.writeLong(node.energyCapacity)
|
||||||
|
packet.writeLong(getMachineMemoryUsed())
|
||||||
|
packet.writeLong(getMachineMemoryTotal())
|
||||||
|
packet.writeLong(getMachineComponentsUsed())
|
||||||
|
packet.writeLong(getMachineComponentsTotal())
|
||||||
|
packet.writeUtf(arch)
|
||||||
}
|
}
|
||||||
|
|
||||||
val redstoneIn = Array(Direction.entries.size) {0}
|
val redstoneIn = Array(Direction.entries.size) {0}
|
||||||
@@ -136,21 +143,56 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun start(): Boolean {
|
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)
|
setRunning(true)
|
||||||
return isOn
|
return isOn
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop(): Boolean {
|
override fun stop(): Boolean {
|
||||||
|
if(!isOn) return false
|
||||||
setRunning(false)
|
setRunning(false)
|
||||||
return isOn
|
return isOn
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun crash(error: String): Boolean {
|
override fun crash(error: String): Boolean {
|
||||||
NeoComputers.LOGGER.warn("Crashing cases is not implemented yet lol")
|
if(isOn) {
|
||||||
return false
|
beepAsync("--")
|
||||||
|
}
|
||||||
|
setRunning(false)
|
||||||
|
err = error
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLastError(): String? = null
|
override fun getLastError(): String? = err
|
||||||
|
|
||||||
override fun getMachineNode(): Networking.Node = node
|
override fun getMachineNode(): Networking.Node = node
|
||||||
|
|
||||||
@@ -165,19 +207,29 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
|||||||
return old
|
return old
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean {
|
override fun beepAsync(pattern: String, frequency: Int, duration: Duration, volume: Double): Boolean {
|
||||||
NeoComputers.LOGGER.warn("beep not yet implemented")
|
NodeSynchronizer.emitBeep(level!!, NodeSynchronizer.BeepDataPayload(getMachineBlockPosition(), pattern, frequency, duration, volume))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
|
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
|
||||||
override fun getMachineMemoryUsed(): Long = 0
|
override fun getMachineMemoryUsed(): Long = 0
|
||||||
override fun getMachineComponentsUsed(): Long = node.connections.size.toLong()
|
override fun getMachineComponentsUsed(): Long = node.getReachable().size.toLong()
|
||||||
override fun getMachineComponentsTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getComponentCapacity(it) }.sum().toLong()
|
override fun getMachineComponentsTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getComponentCapacity(it) }.sum().toLong()
|
||||||
|
override fun getMachineArchitecture() = arch
|
||||||
|
override fun getMachineArchitectures() = stacks.mapNotNull { (it.item as? ComponentItem)?.getArchitecturesProvided(it) }.flatten().toSet()
|
||||||
|
override fun setMachineArchitecture(arch: String) {
|
||||||
|
if(this.arch == arch) return
|
||||||
|
this.arch = arch
|
||||||
|
if(isRunning()) {
|
||||||
|
stop()
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItems(): NonNullList<ItemStack> = stacks
|
override fun getItems(): NonNullList<ItemStack> = stacks
|
||||||
|
|
||||||
override fun stillValid(player: Player): Boolean = true
|
override fun stillValid(player: Player): Boolean = !this.isRemoved
|
||||||
|
|
||||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
super.loadAdditional(compoundTag, provider)
|
super.loadAdditional(compoundTag, provider)
|
||||||
@@ -196,15 +248,19 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
|||||||
override fun getDisplayName(): Component? = Component.literal("Computer")
|
override fun getDisplayName(): Component? = Component.literal("Computer")
|
||||||
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this)
|
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this)
|
||||||
|
|
||||||
override fun canPlaceItem(i: Int, itemStack: ItemStack): Boolean = false
|
|
||||||
override fun canTakeItem(container: Container, i: Int, itemStack: ItemStack): Boolean = false
|
|
||||||
|
|
||||||
override fun setChanged() {
|
|
||||||
super.setChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRemoved() {
|
override fun setRemoved() {
|
||||||
setRunning(false)
|
setRunning(false)
|
||||||
super.setRemoved()
|
super.setRemoved()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun tickNode(level: Level) {
|
||||||
|
super.tickNode(level)
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
if (isRunning()) {
|
||||||
|
if (!node.consumeEnergy(1)) {
|
||||||
|
crash("out of energy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,12 @@ interface MachineEntity {
|
|||||||
fun getMachineBlockPosition(): BlockPos
|
fun getMachineBlockPosition(): BlockPos
|
||||||
fun getMachineLevel(): Level
|
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 isRunning(): Boolean
|
||||||
fun start(): Boolean
|
fun start(): Boolean
|
||||||
@@ -34,6 +39,9 @@ interface MachineEntity {
|
|||||||
fun getMachineMemoryUsed(): Long
|
fun getMachineMemoryUsed(): Long
|
||||||
fun getMachineComponentsUsed(): Long
|
fun getMachineComponentsUsed(): Long
|
||||||
fun getMachineComponentsTotal(): Long
|
fun getMachineComponentsTotal(): Long
|
||||||
|
fun getMachineArchitecture(): String
|
||||||
|
fun getMachineArchitectures(): Set<String>
|
||||||
|
fun setMachineArchitecture(arch: String)
|
||||||
|
|
||||||
// Redstone signals
|
// Redstone signals
|
||||||
fun getRedstoneInput(direction: Direction): Int
|
fun getRedstoneInput(direction: Direction): Int
|
||||||
|
|||||||
@@ -20,20 +20,20 @@ open class CaseMenu : GenericContainerMenu {
|
|||||||
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
|
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
|
||||||
|
|
||||||
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
|
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
|
||||||
this.addInventorySlots(inv, 8, 84)
|
|
||||||
|
|
||||||
val machine = container as? CaseBlockEntity
|
val machine = container as? CaseBlockEntity
|
||||||
|
|
||||||
this.addSlot(ComponentSlot(this.container, 0, 20, 34, machine, eepromRequirement))
|
this.addSlot(ComponentSlot(container, 0, 20, 34, machine, eepromRequirement))
|
||||||
|
|
||||||
var i = 1
|
var i = 1
|
||||||
for ((col, slotCol) in slotRequirements.withIndex()) {
|
for ((col, slotCol) in slotRequirements.withIndex()) {
|
||||||
for ((row, slotReq) in slotCol.withIndex()) {
|
for ((row, slotReq) in slotCol.withIndex()) {
|
||||||
this.addSlot(ComponentSlot(this.container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
|
this.addSlot(ComponentSlot(container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addInventorySlots(inv, 8, 84)
|
||||||
|
|
||||||
// for (int col=1; col<4; col++) {
|
// for (int col=1; col<4; col++) {
|
||||||
// for (int row=1; row<4; row++) {
|
// for (int row=1; row<4; row++) {
|
||||||
// int i = (row-1)*3+(col-1);
|
// int i = (row-1)*3+(col-1);
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
package org.neoflock.neocomputers.gui.screen;
|
package org.neoflock.neocomputers.gui.screen;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder
|
|
||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
|
||||||
import com.mojang.blaze3d.vertex.Tesselator
|
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat
|
|
||||||
import io.netty.buffer.Unpooled
|
import io.netty.buffer.Unpooled
|
||||||
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.gui.GuiGraphics
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
import net.minecraft.client.gui.components.Button
|
|
||||||
import net.minecraft.client.gui.components.ImageButton
|
|
||||||
import net.minecraft.client.gui.components.WidgetSprites
|
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.world.entity.player.Inventory
|
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.NeoComputers
|
||||||
import org.neoflock.neocomputers.block.NodeSynchronizer
|
import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||||
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||||
import org.neoflock.neocomputers.gui.widget.ButtonSprites
|
import org.neoflock.neocomputers.gui.widget.ButtonSprites
|
||||||
import org.neoflock.neocomputers.gui.widget.ImagerButton
|
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 org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
class CaseScreen : GenericContainerScreen<CaseMenu> {
|
class CaseScreen : GenericContainerScreen<CaseMenu> {
|
||||||
private val PCB: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/computer.png")
|
private val PCB: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/computer.png")
|
||||||
@@ -34,11 +32,49 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
|
|||||||
override fun shouldCenterTitle(): Boolean = false
|
override fun shouldCenterTitle(): Boolean = false
|
||||||
|
|
||||||
var isOn = false
|
var isOn = false
|
||||||
|
var lastError: String? = null
|
||||||
|
var energy: Long = 0L
|
||||||
|
var maxEnergy: Long = 0L
|
||||||
|
var memory: Long = 0L
|
||||||
|
var maxMemory: Long = 0L
|
||||||
|
var components: Long = 0L
|
||||||
|
var maxComponents: Long = 0L
|
||||||
|
var arch = ""
|
||||||
|
|
||||||
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||||
super.processScreenStatePacket(buf)
|
super.processScreenStatePacket(buf)
|
||||||
isOn = buf.readBoolean()
|
isOn = buf.readBoolean()
|
||||||
btn?.pressed = isOn
|
btn?.pressed = isOn
|
||||||
|
val error = buf.readByteArray().decodeToString()
|
||||||
|
if(error.isEmpty()) {
|
||||||
|
lastError = null
|
||||||
|
} else {
|
||||||
|
lastError = error
|
||||||
|
}
|
||||||
|
|
||||||
|
energy = buf.readLong()
|
||||||
|
maxEnergy = buf.readLong()
|
||||||
|
memory = buf.readLong()
|
||||||
|
maxMemory = buf.readLong()
|
||||||
|
components = buf.readLong()
|
||||||
|
maxComponents = buf.readLong()
|
||||||
|
arch = buf.readUtf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeButtonTooltip(): List<Component> {
|
||||||
|
val msgs = mutableListOf(Component.literal("Computer " + if(isOn) "ON" else "OFF").withStyle(if(isOn) ChatFormatting.GREEN else ChatFormatting.RED))
|
||||||
|
if(lastError != null) {
|
||||||
|
msgs.addLast(Component.literal("Error: ").withStyle(ChatFormatting.RED).append(Component.literal(lastError!!)))
|
||||||
|
}
|
||||||
|
if(arch.isNotEmpty()) {
|
||||||
|
msgs.addLast(Component.literal("Architecture: $arch"))
|
||||||
|
}
|
||||||
|
if(hasShiftDown()) {
|
||||||
|
msgs.addLast(Component.literal("Energy: $energy / $maxEnergy J").withStyle(if(energy < 100) ChatFormatting.RED else ChatFormatting.WHITE))
|
||||||
|
msgs.addLast(Component.literal("Memory: ${Formatting.formatMemory(memory)} / ${Formatting.formatMemory(maxMemory)}"))
|
||||||
|
msgs.addLast(Component.literal("Components: $components / $maxComponents").withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED))
|
||||||
|
}
|
||||||
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
|
constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
|
||||||
@@ -64,14 +100,20 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
|
|||||||
guiGraphics.blit(PCB, relX, relY, 0, 0, this.imageWidth, this.imageHeight)
|
guiGraphics.blit(PCB, relX, relY, 0, 0, this.imageWidth, this.imageHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
|
||||||
|
super.renderCustomOverlay(graphics, mouseX, mouseY, blend)
|
||||||
|
if(btn!!.isHovered) {
|
||||||
|
graphics.renderTooltip(this.font, computeButtonTooltip(), Optional.empty<TooltipComponent>(), mouseX, mouseY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { // todo: make a better widget system than mojang, practically not even using the fact it's a widget atp
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { // todo: make a better widget system than mojang, practically not even using the fact it's a widget atp
|
||||||
NeoComputers.LOGGER.info(String.format("btn: %d %d %d %d, mouse %s %s", btn!!.x, btn!!.y, btn!!.x+btn!!.width, btn!!.y+btn!!.height, mouseX.toString(), mouseY.toString()))
|
if (button == 0 && btn!!.isHovered) {
|
||||||
if (button != 0) return false
|
|
||||||
if (btn!!.x < mouseX.toInt() && mouseX.toInt() < btn!!.x+btn!!.width && btn!!.y < mouseY.toInt() && mouseY.toInt() < btn!!.y+btn!!.height) {
|
|
||||||
btn!!.playDownSound(Minecraft.getInstance().soundManager)
|
btn!!.playDownSound(Minecraft.getInstance().soundManager)
|
||||||
btn!!.onClick(mouseX, mouseY)
|
btn!!.onClick(mouseX, mouseY)
|
||||||
return true
|
return true
|
||||||
} else return false
|
}
|
||||||
|
return super.mouseClicked(mouseX, mouseY, button)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@ open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties(
|
|||||||
|
|
||||||
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents
|
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents
|
||||||
|
|
||||||
|
override fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf("Lua 5.3")
|
||||||
|
|
||||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
|
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ interface ComponentItem {
|
|||||||
// Get machine properties they can influence
|
// Get machine properties they can influence
|
||||||
fun getMemoryCapacity(itemStack: ItemStack): Int = 0
|
fun getMemoryCapacity(itemStack: ItemStack): Int = 0
|
||||||
fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf()
|
||||||
|
|
||||||
// Component placed, node must now exist
|
// Component placed, node must now exist
|
||||||
fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
|
fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
|
||||||
|
|||||||
@@ -19,4 +19,11 @@ object DataComponents {
|
|||||||
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
||||||
val EEPROM_DATA = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_data"),
|
val EEPROM_DATA = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_data"),
|
||||||
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
||||||
|
val EEPROM_CODESIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_codesize"),
|
||||||
|
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())
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@ import java.nio.ByteBuffer
|
|||||||
fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
|
fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
|
||||||
.component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
|
.component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
|
||||||
.component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
|
.component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
|
||||||
|
.component(DataComponents.EEPROM_CODESIZE, 0)
|
||||||
|
.component(DataComponents.EEPROM_DATASIZE, 0)
|
||||||
.component(DataComponents.LABEL, "")
|
.component(DataComponents.LABEL, "")
|
||||||
.component(DataComponents.READONLY, false)
|
.component(DataComponents.READONLY, false)
|
||||||
|
|
||||||
@@ -39,8 +41,8 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
|
|||||||
tooltipFlag: TooltipFlag
|
tooltipFlag: TooltipFlag
|
||||||
) {
|
) {
|
||||||
if(tooltipFlag.isAdvanced) {
|
if(tooltipFlag.isAdvanced) {
|
||||||
val codeSize = itemStack.get(DataComponents.EEPROM_CODE)?.position() ?: 0
|
val codeSize = itemStack.get(DataComponents.EEPROM_CODESIZE) ?: 0
|
||||||
val dataSize = itemStack.get(DataComponents.EEPROM_DATA)?.position() ?: 0
|
val dataSize = itemStack.get(DataComponents.EEPROM_DATASIZE) ?: 0
|
||||||
val addr = itemStack.get(DataComponents.ADDRESS)
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
val readonly = itemStack.get(DataComponents.READONLY) ?: false
|
val readonly = itemStack.get(DataComponents.READONLY) ?: false
|
||||||
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ object Tabs {
|
|||||||
codeBuf.put(code)
|
codeBuf.put(code)
|
||||||
luaBios.set(DataComponents.LABEL, "Lua BIOS")
|
luaBios.set(DataComponents.LABEL, "Lua BIOS")
|
||||||
luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
|
luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
|
||||||
|
luaBios.set(DataComponents.EEPROM_CODESIZE, code.size)
|
||||||
output.accept(luaBios)
|
output.accept(luaBios)
|
||||||
} while(false)
|
} while(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import net.minecraft.world.item.TooltipFlag
|
|||||||
import org.neoflock.neocomputers.entity.MachineEntity
|
import org.neoflock.neocomputers.entity.MachineEntity
|
||||||
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
import org.neoflock.neocomputers.network.Networking
|
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
|
// yes, we're counting TUNNEL as a conventional networking card
|
||||||
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.NETWORK)
|
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 addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
list.addLast(addrComp)
|
list.addLast(addrComp)
|
||||||
|
list.addLast(Component.translatable("neocomputers.tunnel.channel", itemStack.get(DataComponents.TUNNEL_CHANNEL) ?: "creative"))
|
||||||
// TODO: show max packet size and whatnot
|
// TODO: show max packet size and whatnot
|
||||||
}
|
}
|
||||||
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
|||||||
@@ -246,8 +246,8 @@ object Networking {
|
|||||||
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
|
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
val wirelessNodes = mutableSetOf<WirelessEndpoint>()
|
val wirelessNodes = ThreadLocal.withInitial { mutableSetOf<WirelessEndpoint>() }
|
||||||
val allNodes = mutableMapOf<UUID, Node>()
|
val allNodes = ThreadLocal.withInitial { mutableMapOf<UUID, Node>() }
|
||||||
|
|
||||||
// node may differ from message.sender in the case of relays,
|
// node may differ from message.sender in the case of relays,
|
||||||
// as they might have DIRECT reachability but
|
// as they might have DIRECT reachability but
|
||||||
@@ -267,7 +267,7 @@ object Networking {
|
|||||||
val startPos = starter.getPosition();
|
val startPos = starter.getPosition();
|
||||||
val startDim = starter.getDimension();
|
val startDim = starter.getDimension();
|
||||||
val range = starter.getRange();
|
val range = starter.getRange();
|
||||||
wirelessNodes.forEach {
|
wirelessNodes.get().forEach {
|
||||||
if(it.getDimension() != startDim) return;
|
if(it.getDimension() != startDim) return;
|
||||||
val pos = it.getPosition();
|
val pos = it.getPosition();
|
||||||
val d = distanceBetween(startPos, pos);
|
val d = distanceBetween(startPos, pos);
|
||||||
@@ -279,27 +279,29 @@ object Networking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tickAllNodes() {
|
fun tickAllNodes() {
|
||||||
allNodes.forEach { it.value.tick() }
|
allNodes.get().forEach { it.value.tick() }
|
||||||
tickCount++
|
tickCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNode(address: UUID): Node? = allNodes[address]
|
fun getNode(address: UUID): Node? = allNodes.get()[address]
|
||||||
|
|
||||||
// TODO: use setter, more convenient
|
// TODO: use setter, more convenient
|
||||||
fun changeNodeAddress(node: Node, address: UUID) {
|
fun changeNodeAddress(node: Node, address: UUID) {
|
||||||
allNodes.remove(node.address)
|
if(node.address.equals(address)) return
|
||||||
|
if(node.address !in allNodes.get()) return
|
||||||
|
allNodes.get().remove(node.address)
|
||||||
node.address = address
|
node.address = address
|
||||||
allNodes[address] = node
|
allNodes.get()[address] = node
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNode(node: Node) {
|
fun addNode(node: Node) {
|
||||||
if(node.address in allNodes) return;
|
if(node.address in allNodes.get()) return
|
||||||
allNodes[node.address] = node
|
allNodes.get()[node.address] = node
|
||||||
if(node is WirelessEndpoint) {
|
if(node is WirelessEndpoint) {
|
||||||
wirelessNodes.add(node);
|
wirelessNodes.get().add(node);
|
||||||
}
|
}
|
||||||
// notify at the end so it is notified of its own creation
|
// notify at the end so it is notified of its own creation
|
||||||
allNodes.forEach { it.value.onNodeAdded(node) }
|
allNodes.get().forEach { it.value.onNodeAdded(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNodes(vararg nodes: Node) {
|
fun addNodes(vararg nodes: Node) {
|
||||||
@@ -307,16 +309,16 @@ object Networking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun removeNode(node: Node) {
|
fun removeNode(node: Node) {
|
||||||
if(node.address !in allNodes) return
|
if(node.address !in allNodes.get()) return
|
||||||
allNodes.forEach { it.value.onNodeRemoved(node) }
|
allNodes.get().forEach { it.value.onNodeRemoved(node) }
|
||||||
// toList() in order to copy it
|
// toList() in order to copy it
|
||||||
node.connections.toList().forEach {
|
node.connections.toList().forEach {
|
||||||
node.disconnectFrom(it)
|
node.disconnectFrom(it)
|
||||||
}
|
}
|
||||||
// actually remove at the end so it can listen to its own removal
|
// actually remove at the end so it can listen to its own removal
|
||||||
allNodes.remove(node.address)
|
allNodes.get().remove(node.address)
|
||||||
if(node is WirelessEndpoint) {
|
if(node is WirelessEndpoint) {
|
||||||
wirelessNodes.remove(node);
|
wirelessNodes.get().remove(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,25 +326,28 @@ object Networking {
|
|||||||
nodes.forEach { removeNode(it) }
|
nodes.forEach { removeNode(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val channels = mutableMapOf<String, MutableSet<Node>>();
|
val channels = ThreadLocal.withInitial { mutableMapOf<String, MutableSet<Node>>() }
|
||||||
|
|
||||||
fun addToChannel(channel: String, node: Node) {
|
fun addToChannel(channel: String, node: Node) {
|
||||||
if(!channels.containsKey(channel)) {
|
val localChannels = channels.get()
|
||||||
channels[channel] = mutableSetOf();
|
if(!localChannels.containsKey(channel)) {
|
||||||
|
localChannels[channel] = mutableSetOf();
|
||||||
}
|
}
|
||||||
channels[channel]!!.add(node);
|
localChannels[channel]!!.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeFromChannel(channel: String, node: Node) {
|
fun removeFromChannel(channel: String, node: Node) {
|
||||||
if(!channels.containsKey(channel)) return;
|
val localChannels = channels.get()
|
||||||
channels[channel]?.remove(node);
|
if(!localChannels.containsKey(channel)) return;
|
||||||
if(channels[channel].isNullOrEmpty()) {
|
localChannels[channel]?.remove(node);
|
||||||
channels.remove(channel);
|
if(localChannels[channel].isNullOrEmpty()) {
|
||||||
|
localChannels.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun emitChannelMessage(starter: Node, channel: String, message: Message) {
|
fun emitChannelMessage(starter: Node, channel: String, message: Message) {
|
||||||
val c = channels[channel] ?: return;
|
val localChannels = channels.get()
|
||||||
|
val c = localChannels[channel] ?: return;
|
||||||
c.forEach { if(it != starter) it.received(message); };
|
c.forEach { if(it != starter) it.received(message); };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,20 @@
|
|||||||
package org.neoflock.neocomputers.sounds
|
package org.neoflock.neocomputers.sounds
|
||||||
|
|
||||||
import dev.architectury.registry.registries.DeferredRegister
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.core.registries.Registries
|
import net.minecraft.core.registries.Registries
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.sounds.SoundEvent
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import org.lwjgl.BufferUtils
|
||||||
import org.neoflock.neocomputers.NeoComputers
|
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 {
|
object Sounds {
|
||||||
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
|
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
|
||||||
@@ -14,4 +24,145 @@ object Sounds {
|
|||||||
fun registerSound(name: String) = SOUNDS.register(name) {
|
fun registerSound(name: String) = SOUNDS.register(name) {
|
||||||
SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, 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.lan": "Wired Network Card",
|
||||||
"item.neocomputers.wlan0": "Wireless Network Card (Tier 1)",
|
"item.neocomputers.wlan0": "Wireless Network Card (Tier 1)",
|
||||||
"item.neocomputers.wlan1": "Wireless Network Card (Tier 2)",
|
"item.neocomputers.wlan1": "Wireless Network Card (Tier 2)",
|
||||||
|
"item.neocomputers.tunnel": "Linked Card",
|
||||||
"item.neocomputers.data0": "Data Card (Tier 1)",
|
"item.neocomputers.data0": "Data Card (Tier 1)",
|
||||||
"item.neocomputers.data1": "Data Card (Tier 2)",
|
"item.neocomputers.data1": "Data Card (Tier 2)",
|
||||||
"item.neocomputers.data2": "Data Card (Tier 3)",
|
"item.neocomputers.data2": "Data Card (Tier 3)",
|
||||||
@@ -47,5 +48,6 @@
|
|||||||
"neocomputers.memory.capacity": "Capacity: %1$s",
|
"neocomputers.memory.capacity": "Capacity: %1$s",
|
||||||
"neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s",
|
"neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s",
|
||||||
"neocomputers.eeprom.dataused": "Data 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"
|
"sounds.neocomputers.computer_running": "Computer Fans"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user