more work on computer stuff

This commit is contained in:
2026-04-22 18:02:45 +02:00
parent c8da0aba68
commit c98ec2fc99
13 changed files with 172 additions and 58 deletions

View File

@@ -4,6 +4,7 @@ import dev.architectury.event.events.client.ClientLifecycleEvent
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
@@ -73,13 +74,18 @@ object NeoComputers {
NodeSynchronizer.playerScreenClosed(player) NodeSynchronizer.playerScreenClosed(player)
} }
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, { // networking has no way to define a C2S packet type, so we need the listener on both
packet, ctx -> // however, defining it separately on both breaks both ends
val player = ctx.player // so we define it once, but on both platforms
if(player is ServerPlayer) { if(Platform.getEnvironment() == Env.CLIENT || Platform.getEnvironment() == Env.SERVER) {
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer) NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
} packet, ctx ->
}) val player = ctx.player
if(player is ServerPlayer) {
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) {{

View File

@@ -24,6 +24,7 @@ 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
object NodeSynchronizer { object NodeSynchronizer {
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload { class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
@@ -134,7 +135,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)
@@ -202,6 +203,9 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
override fun setRemoved() { override fun setRemoved() {
super.setRemoved() super.setRemoved()
if(node.powerRole == PowerRole.GENERATOR) {
NeoComputers.LOGGER.info("removed generator ${node.address}")
}
Networking.removeNode(node) Networking.removeNode(node)
} }

View File

@@ -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())
} }
} }

View File

@@ -40,6 +40,8 @@ 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() {
@@ -70,6 +72,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}
@@ -120,6 +130,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
isOn = value isOn = value
val world = level ?: return val world = level ?: return
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn) blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
if(value) beepAsync(8000, Duration.ofSeconds(1), 1.0)
if(world.isClientSide) { if(world.isClientSide) {
if(value) { if(value) {
soundInstance = ComputerRunningSoundInstance(this, Sounds.COMPUTER_RUNNING.get(), SoundSource.AMBIENT) soundInstance = ComputerRunningSoundInstance(this, Sounds.COMPUTER_RUNNING.get(), SoundSource.AMBIENT)
@@ -136,6 +147,19 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
} }
override fun start(): Boolean { override fun start(): Boolean {
err = null
if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
crash("too many components")
return false
}
if(node.energy < 100) {
crash("not enough energy")
return false
}
if(getMachineMemoryTotal() == 0L) {
crash("no memory provided")
return false
}
setRunning(true) setRunning(true)
return isOn return isOn
} }
@@ -146,11 +170,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
} }
override fun crash(error: String): Boolean { override fun crash(error: String): Boolean {
NeoComputers.LOGGER.warn("Crashing cases is not implemented yet lol") setRunning(false)
return 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
@@ -172,12 +197,22 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
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,9 +231,6 @@ 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() { override fun setChanged() {
super.setChanged() super.setChanged()
} }
@@ -207,4 +239,15 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
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")
}
}
}
}
} }

View File

@@ -34,6 +34,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

View File

@@ -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);

View File

@@ -1,26 +1,22 @@
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 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.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 +30,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 +98,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)
} }
} }

View File

@@ -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
} }

View File

@@ -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) {

View File

@@ -19,4 +19,8 @@ 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())
} }

View File

@@ -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)

View File

@@ -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)
} }

View File

@@ -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,30 @@ 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
NeoComputers.LOGGER.info("remapping node from ${node.address} to $address")
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 NeoComputers.LOGGER.info("adding node ${node.address}")
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 +310,17 @@ 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) } NeoComputers.LOGGER.info("removing node ${node.address}")
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 +328,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); };
} }
} }