clean code, acceptable performance

This commit is contained in:
2026-04-26 16:05:46 +00:00
parent ed6ddddf98
commit 8edd36124b
24 changed files with 365 additions and 341 deletions

View File

@@ -4,38 +4,35 @@ import net.minecraft.client.player.LocalPlayer
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.ChatType
import net.minecraft.network.chat.OutgoingChatMessage import net.minecraft.network.chat.OutgoingChatMessage
import net.minecraft.network.chat.PlayerChatMessage import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType 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 net.minecraft.world.phys.BlockHitResult import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.PowerRole import org.neoflock.neocomputers.network.PowerRole
import kotlin.math.min import kotlin.math.min
open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : NodeBlockEntity(type, pos, state) { open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : NodeBlockEntity(type, pos, state) {
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.STORAGE override var powerRole = PowerRole.STORAGE
override var energyCapacity: Long = capacity override var energyCapacity: Long = capacity
} }
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider) super.loadAdditional(compoundTag, provider)
node.energy = min(compoundTag.getLong("energy"), node.energyCapacity) deviceNode.energy = min(compoundTag.getLong("energy"), deviceNode.energyCapacity)
} }
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider) super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy) compoundTag.putLong("energy", deviceNode.energy)
} }
} }
@@ -65,8 +62,8 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
val p = player as LocalPlayer val p = player as LocalPlayer
val ent = level.getBlockEntity(blockPos) val ent = level.getBlockEntity(blockPos)
if(ent is CapacitorEntity) { if(ent is CapacitorEntity) {
if(p.isCrouching) ent.node.giveEnergy(1) if(p.isCrouching) ent.deviceNode.giveEnergy(1)
val msg = PlayerChatMessage.system("energy: ${ent.node.energy} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.node.getReachable().size} connected)") val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.deviceNode.getReachable().size} connected)")
p.sendSystemMessage(OutgoingChatMessage.create(msg).content()) p.sendSystemMessage(OutgoingChatMessage.create(msg).content())
} }
} }

View File

@@ -21,6 +21,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.DeviceNode
import java.time.Duration import java.time.Duration
object NodeSynchronizer { object NodeSynchronizer {
@@ -148,30 +149,30 @@ object NodeSynchronizer {
} }
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) {
abstract val node: Networking.Node abstract val deviceNode: DeviceNode
fun initNetworking(): NodeBlockEntity { fun initNetworking(): NodeBlockEntity {
Networking.addNode(node) Networking.addNode(deviceNode)
invalidateNodeState() invalidateNodeState()
return this return this
} }
// runs on the server, meant to encode state to send to all players // runs on the server, meant to encode state to send to all players
open fun encodeDownstreamData(packet: FriendlyByteBuf) { open fun encodeDownstreamData(packet: FriendlyByteBuf) {
packet.writeUUID(node.address) packet.writeUUID(deviceNode.address)
packet.writeLong(node.energy) packet.writeLong(deviceNode.energy)
packet.writeLong(node.energyCapacity) packet.writeLong(deviceNode.energyCapacity)
packet.writeEnum(node.reachability) packet.writeEnum(deviceNode.reachability)
packet.writeEnum(node.powerRole) packet.writeEnum(deviceNode.powerRole)
} }
// 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) {
Networking.changeNodeAddress(node, packet.readUUID()) Networking.changeNodeAddress(deviceNode, packet.readUUID())
node.energy = packet.readLong() deviceNode.energy = packet.readLong()
node.energyCapacity = packet.readLong() deviceNode.energyCapacity = packet.readLong()
node.reachability = packet.readEnum(node.reachability.javaClass) deviceNode.reachability = packet.readEnum(deviceNode.reachability.javaClass)
node.powerRole = packet.readEnum(node.powerRole.javaClass) deviceNode.powerRole = packet.readEnum(deviceNode.powerRole.javaClass)
} }
// Encodes data meant for the associated screen of a player // Encodes data meant for the associated screen of a player
@@ -223,7 +224,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
if(!stateIsDirty) return if(!stateIsDirty) return
stateIsDirty = false stateIsDirty = false
computeEdges().forEach { computeEdges().forEach {
node.connectTo(it.node) deviceNode.connectTo(it.deviceNode)
} }
} }
@@ -235,7 +236,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
override fun setRemoved() { override fun setRemoved() {
super.setRemoved() super.setRemoved()
Networking.removeNode(node) Networking.removeNode(deviceNode)
} }
override fun clearRemoved() { override fun clearRemoved() {
@@ -259,7 +260,7 @@ abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(pr
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() if(Networking.getNode(blockEntity.deviceNode.address) == null) blockEntity.initNetworking()
blockEntity.tickNode(level) blockEntity.tickNode(level)
} }
} }

View File

@@ -7,14 +7,13 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.BlockGetter import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.RedStoneWireBlock
import net.minecraft.world.level.block.RedstoneTorchBlock
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.DeviceNode
fun dirToIdx(direction: Direction) = Direction.entries.indexOf(direction) fun dirToIdx(direction: Direction) = Direction.entries.indexOf(direction)
@@ -23,7 +22,7 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
val redstoneOut = Array<Int>(Direction.entries.size) {0} val redstoneOut = Array<Int>(Direction.entries.size) {0}
// TODO: have redstone I/O node for component and shi // TODO: have redstone I/O node for component and shi
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
} }
@@ -42,7 +41,7 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
} }
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) { fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
Networking.emitMessage(node, Networking.ComputerUncheckedSignal(node, "redstone_changed", arrayOf(node.address.toString(), dirToIdx(dir), oldValue, newValue))) Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.address.toString(), dirToIdx(dir), oldValue, newValue)))
NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue") NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue")
} }
} }

View File

@@ -7,32 +7,23 @@ import net.minecraft.core.Direction
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.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.EnumProperty import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.block.state.properties.EnumProperty.* import net.minecraft.world.level.block.state.properties.EnumProperty.*
import net.minecraft.world.level.block.state.properties.IntegerProperty import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.phys.BlockHitResult import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.ScreenEntity import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.Menus
import org.neoflock.neocomputers.gui.menu.ScreenMenu import org.neoflock.neocomputers.gui.menu.ScreenMenu
import org.neoflock.neocomputers.network.Networking
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
@@ -62,7 +53,7 @@ class ScreenBlock() : NodeBlock() {
): InteractionResult { ): InteractionResult {
if(!level.isClientSide) { if(!level.isClientSide) {
val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get() val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
if(!screenState.node.consumeEnergy(ENERGY)) { if(!screenState.deviceNode.consumeEnergy(ENERGY)) {
player.sendSystemMessage(Component.literal("Not enough power.")) player.sendSystemMessage(Component.literal("Not enough power."))
return InteractionResult.SUCCESS return InteractionResult.SUCCESS
}; };

View File

@@ -25,6 +25,7 @@ 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
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance
@@ -44,7 +45,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
var arch = "Lua 5.3" var arch = "Lua 5.3"
var soundInstance: SoundInstance? = null var soundInstance: SoundInstance? = null
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.CONSUMER override var powerRole = PowerRole.CONSUMER
override var energyCapacity: Long = 500 override var energyCapacity: Long = 500
} }
@@ -79,8 +80,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
super.encodeScreenData(player, packet) super.encodeScreenData(player, packet)
packet.writeBoolean(isOn) packet.writeBoolean(isOn)
packet.writeByteArray((err ?: "").encodeToByteArray()) packet.writeByteArray((err ?: "").encodeToByteArray())
packet.writeLong(node.energy) packet.writeLong(deviceNode.energy)
packet.writeLong(node.energyCapacity) packet.writeLong(deviceNode.energyCapacity)
packet.writeLong(getMachineMemoryUsed()) packet.writeLong(getMachineMemoryUsed())
packet.writeLong(getMachineMemoryTotal()) packet.writeLong(getMachineMemoryTotal())
packet.writeLong(getMachineComponentsUsed()) packet.writeLong(getMachineComponentsUsed())
@@ -112,12 +113,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
item.onMachineEvent(it, this, event) item.onMachineEvent(it, this, event)
} }
} }
Networking.emitMessage(node, Networking.ComputerEvent(node, event)) Networking.emitMessage(deviceNode, Networking.ComputerEvent(deviceNode, event))
} }
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) { fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue)) sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue))
Networking.emitMessage(node, Networking.ComputerUncheckedSignal(node, "redstone_changed", arrayOf(node.address.toString(), dirToIdx(dir), oldValue, newValue))) Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.address.toString(), dirToIdx(dir), oldValue, newValue)))
NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue") NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue")
if(oldValue == 0) { if(oldValue == 0) {
// Rising edge // Rising edge
@@ -133,7 +134,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
fun setRunning(value: Boolean) { fun setRunning(value: Boolean) {
if(isOn == value) return if(isOn == value) return
NeoComputers.LOGGER.info("[${node.address}] Going from $isOn to $value") NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value")
isOn = value isOn = value
val world = level ?: return val world = level ?: return
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn) blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
@@ -167,10 +168,10 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
return false return false
} }
// less than 20% energy is bad // less than 20% energy is bad
if(node.energy < node.energyCapacity/5) { if(deviceNode.energy < deviceNode.energyCapacity/5) {
crash("@neocomputers.errors.ENOENJ") crash("@neocomputers.errors.ENOENJ")
// we add a beep for the special case where we do have a little bit of energy :P // we add a beep for the special case where we do have a little bit of energy :P
if(node.energy > 0) beepAsync("..") if(deviceNode.energy > 0) beepAsync("..")
return false return false
} }
if(getMachineMemoryTotal() == 0L) { if(getMachineMemoryTotal() == 0L) {
@@ -205,7 +206,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun getLastError(): String? = err override fun getLastError(): String? = err
override fun getMachineNode(): Networking.Node = node override fun getMachineNode() = deviceNode
override fun getRedstoneInput(direction: Direction): Int = redstoneIn[dirToIdx(direction)] override fun getRedstoneInput(direction: Direction): Int = redstoneIn[dirToIdx(direction)]
@@ -233,7 +234,7 @@ 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.getReachable().size.toLong() override fun getMachineComponentsUsed(): Long = deviceNode.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 getMachineArchitecture() = arch
override fun getMachineArchitectures() = stacks.mapNotNull { (it.item as? ComponentItem)?.getArchitecturesProvided(it) }.flatten().toSet() override fun getMachineArchitectures() = stacks.mapNotNull { (it.item as? ComponentItem)?.getArchitecturesProvided(it) }.flatten().toSet()
@@ -252,14 +253,14 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider) super.loadAdditional(compoundTag, provider)
node.energy = min(node.energyCapacity, compoundTag.getLong("energy")) deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
//isOn = compoundTag.getBoolean("powerOn") //isOn = compoundTag.getBoolean("powerOn")
ContainerHelper.loadAllItems(compoundTag, getItems(), provider) ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
} }
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider) super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy) compoundTag.putLong("energy", deviceNode.energy)
//compoundTag.putBoolean("powerOn", isOn) //compoundTag.putBoolean("powerOn", isOn)
ContainerHelper.saveAllItems(compoundTag, getItems(), provider) ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
} }
@@ -278,11 +279,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
if (isRunning()) { if (isRunning()) {
if(diskActivityTime > 0) diskActivityTime-- if(diskActivityTime > 0) diskActivityTime--
if(networkActivityTime > 0) networkActivityTime-- if(networkActivityTime > 0) networkActivityTime--
if(getMachineComponentsUsed() > getMachineComponentsTotal()) { if(getMachineArchitectures().isEmpty()) {
crash("too many components") crash("@neocomputers.errors.ENOCPU")
} } else if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
if (!node.consumeEnergy(1)) { crash("@neocomputers.errors.E2BIG")
crash("out of energy") } else if (!deviceNode.consumeEnergy(1)) {
crash("@neocomputers.errors.ENOENJ")
} }
} }
} }

View File

@@ -17,6 +17,7 @@ import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CombustionGeneratorBlock import org.neoflock.neocomputers.block.CombustionGeneratorBlock
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.utils.GenericContainer import org.neoflock.neocomputers.utils.GenericContainer
@@ -28,7 +29,7 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
var burningTimeRemaining: Int = 0 var burningTimeRemaining: Int = 0
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.GENERATOR override var powerRole = PowerRole.GENERATOR
override var energyCapacity: Long = 100000 override var energyCapacity: Long = 100000
} }
@@ -52,13 +53,13 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
// keep combusting and shi // keep combusting and shi
if(burningTimeRemaining > 0) { if(burningTimeRemaining > 0) {
burningTimeRemaining-- burningTimeRemaining--
node.giveEnergy(energyPerTick) deviceNode.giveEnergy(energyPerTick)
setChanged() setChanged()
return return
} }
// no point // no point
if(node.energy >= node.energyCapacity) return; if(deviceNode.energy >= deviceNode.energyCapacity) return;
// :fire: // :fire:
val fuel = stacks[0] val fuel = stacks[0]
@@ -79,20 +80,20 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
} }
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) { override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
packet.writeLong(node.energy) packet.writeLong(deviceNode.energy)
packet.writeLong(node.energyCapacity) packet.writeLong(deviceNode.energyCapacity)
} }
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider) super.loadAdditional(compoundTag, provider)
node.energy = min(node.energyCapacity, compoundTag.getLong("energy")) deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
burningTimeRemaining = compoundTag.getInt("burningTimeRemaining") burningTimeRemaining = compoundTag.getInt("burningTimeRemaining")
ContainerHelper.loadAllItems(compoundTag, getItems(), provider) ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
} }
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider) super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy) compoundTag.putLong("energy", deviceNode.energy)
compoundTag.putInt("burningTimeRemaining", burningTimeRemaining) compoundTag.putInt("burningTimeRemaining", burningTimeRemaining)
ContainerHelper.saveAllItems(compoundTag, getItems(), provider) ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
} }

View File

@@ -5,6 +5,7 @@ import net.minecraft.core.Direction
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.item.ComponentItem import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import java.time.Duration import java.time.Duration
@@ -39,7 +40,7 @@ interface MachineEntity {
fun crash(error: String): Boolean fun crash(error: String): Boolean
fun getLastError(): String? fun getLastError(): String?
fun getMachineNode(): Networking.Node fun getMachineNode(): DeviceNode
// Some metadata // Some metadata
fun getMachineMemoryTotal(): Long fun getMachineMemoryTotal(): Long

View File

@@ -1,8 +1,6 @@
package org.neoflock.neocomputers.entity; package org.neoflock.neocomputers.entity;
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
@@ -11,15 +9,15 @@ import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.gui.buffer.BufferRenderer import org.neoflock.neocomputers.gui.buffer.BufferRenderer
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.utils.GPUChar import org.neoflock.neocomputers.utils.GPUChar
import org.neoflock.neocomputers.utils.TextBuffer import org.neoflock.neocomputers.utils.TextBuffer
class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
NodeBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) { NodeBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
override fun received(message: Networking.Message) { override fun received(message: Networking.Message) {
super.received(message) super.received(message)
if(message is Networking.ComputerEvent) { if(message is Networking.ComputerEvent) {
@@ -71,8 +69,7 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
} }
private fun createscreenstuffs() { private fun createscreenstuffs() {
bound = "screen/"+node.address.toString().replace("-", "_") bound = "screen/"+deviceNode.address.toString().replace("-", "_")
NeoComputers.LOGGER.info(bound)
if (level!!.isClientSide) { if (level!!.isClientSide) {
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf) var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
renderer.drawBuffer() renderer.drawBuffer()

View File

@@ -6,13 +6,14 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole import org.neoflock.neocomputers.network.PowerRole
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) { class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
val energyPerTick: Long = 50 val energyPerTick: Long = 50
override val node = object : Networking.Node() { override val deviceNode = object : DeviceNode() {
override var powerRole: PowerRole = PowerRole.GENERATOR override var powerRole: PowerRole = PowerRole.GENERATOR
override var energyCapacity: Long = 50000 override var energyCapacity: Long = 50000
} }
@@ -21,17 +22,17 @@ class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : No
super.tickNode(level) super.tickNode(level)
val l = level val l = level
if(l.isDay) { if(l.isDay) {
node.giveEnergy(energyPerTick) deviceNode.giveEnergy(energyPerTick)
} }
} }
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider) super.loadAdditional(compoundTag, provider)
node.energy = compoundTag.getLong("energy") deviceNode.energy = compoundTag.getLong("energy")
} }
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider) super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy) compoundTag.putLong("energy", deviceNode.energy)
} }
} }

View File

@@ -7,13 +7,13 @@ import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
open class CBUSItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), ComponentItem { open class CBUSItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), ComponentItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.BUS) override fun getComponentRoles(itemStack: ItemStack) = setOf(ComponentRoles.BUS)
override fun getComponentTier(itemStack: ItemStack): Int = tier override fun getComponentTier(itemStack: ItemStack) = tier
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents override fun getComponentCapacity(itemStack: ItemStack) = maxComponents
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
} }
class CBUS0: CBUSItem(1, 8) class CBUS0: CBUSItem(1, 8)
class CBUS1: CBUSItem(2, 12) class CBUS1: CBUSItem(2, 12)

View File

@@ -15,7 +15,7 @@ open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties(
override fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf("Lua 5.3") 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?) = null
} }
class CPU0: CPUItem(1, 8) class CPU0: CPUItem(1, 8)

View File

@@ -3,6 +3,7 @@ package org.neoflock.neocomputers.item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.entity.MachineEntity import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.MachineEvent import org.neoflock.neocomputers.entity.MachineEvent
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import java.util.UUID import java.util.UUID
@@ -33,10 +34,10 @@ interface ComponentItem {
} }
// To node, if applicable. Meant to create the node, but not add it, as it will use the itemStack's address to find it again // To node, if applicable. Meant to create the node, but not add it, as it will use the itemStack's address to find it again
fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): DeviceNode?
// Gets the node associated to an item, if it exists // Gets the node associated to an item, if it exists
fun getComponentNode(itemStack: ItemStack): Networking.Node? { fun getComponentNode(itemStack: ItemStack): DeviceNode? {
val address = itemStack.get(DataComponents.ADDRESS) ?: return null val address = itemStack.get(DataComponents.ADDRESS) ?: return null
val uuid = UUID.fromString(address) ?: return null val uuid = UUID.fromString(address) ?: return null
return Networking.getNode(uuid) return Networking.getNode(uuid)

View File

@@ -22,7 +22,7 @@ open class DataCard(val tier: Int, val limit: Long): Item(Properties()), Compone
} }
// TODO: Modem Component // TODO: Modem Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -32,7 +32,7 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
super.whenComponentPlaced(itemStack, machine, newRole) super.whenComponentPlaced(itemStack, machine, newRole)
} }
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -21,7 +21,7 @@ open class GPUCard(val tier: Int, val vram: Long): Item(Properties()), Component
} }
// TODO: GPU Component // TODO: GPU Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -28,7 +28,7 @@ open class HardDiskItem(val tier: Int, val capacity: Long): Item(getDiskProperti
super.whenComponentPlaced(itemStack, machine, newRole) super.whenComponentPlaced(itemStack, machine, newRole)
} }
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -20,7 +20,7 @@ class InternetCard: Item(Item.Properties()), ComponentItem {
} }
// TODO: Internet Component // TODO: Internet Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -6,6 +6,7 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag 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.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting import org.neoflock.neocomputers.utils.Formatting
@@ -19,7 +20,7 @@ open class MemoryItem(val tier: Int, val capacity: Int): Item(Item.Properties().
override fun getComponentCapacity(itemStack: ItemStack): Int = 0 override fun getComponentCapacity(itemStack: ItemStack): Int = 0
// no node for memory // no node for memory
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -19,7 +19,7 @@ open class NetworkCard(val tier: Int, val maxRange: Int, val isWired: Boolean):
} }
// TODO: Modem Component // TODO: Modem Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -21,7 +21,7 @@ open class RedstoneCard(val tier: Int): Item(Properties()), ComponentItem {
} }
// TODO: Redstone Component // TODO: Redstone Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -20,7 +20,7 @@ class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "cr
} }
// TODO: Tunnel Component // TODO: Tunnel Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun appendHoverText( override fun appendHoverText(
itemStack: ItemStack, itemStack: ItemStack,

View File

@@ -0,0 +1,235 @@
package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos
import net.minecraft.network.FriendlyByteBuf
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.network.Networking.Message
import org.neoflock.neocomputers.network.Networking.Visibility
import org.neoflock.neocomputers.network.Networking.maxHopCount
import java.util.UUID
import kotlin.math.min
open class DeviceNode(_address: UUID? = null) {
val connections = HashSet<DeviceNode>()
private var reachableCache: Set<DeviceNode>? = null
var address = _address ?: UUID.randomUUID()
open var reachability = Visibility.NETWORK
open var powerRole = PowerRole.CONSUMER
open var energy: Long = 0
open var energyCapacity: Long = 0
// give energy, returns how much was actually given
// cannot exceed amount specified
open fun giveEnergy(amount: Long): Long {
val maximum = min(amount, energyCapacity - energy)
energy += maximum
markChanged()
return maximum
}
// take energy out, returns how much was actually taken
// cannot exceed amount specified
open fun withdrawEnergy(amount: Long): Long {
val maximum = min(amount, energy)
energy -= maximum
markChanged()
return maximum
}
fun getChargerNodes(): Set<DeviceNode> = getReachable().filter { it.powerRole != PowerRole.CONSUMER }.toSet()
fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energy }
fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energyCapacity }
// attempts to consume
fun consumeEnergy(energy: Long): Boolean {
// consumes energy, returns false if not enough
val total = totalEnergyInConnections() + this.energy
if(energy > total) return false
var remaining = energy
remaining -= withdrawEnergy(remaining)
if(remaining <= 0) return true
for (charger in getChargerNodes()) {
if(remaining <= 0) break
remaining -= charger.withdrawEnergy(remaining)
}
return true
}
// PLEASE only call if consumer, in the name of all that is holy
fun tryToChargeFully() {
var remaining = energyCapacity - energy
if(remaining <= 0) return
for (charger in getChargerNodes()) {
if(remaining <= 0) break
val amount = charger.withdrawEnergy(remaining)
val given = giveEnergy(amount)
remaining -= given
if(given < amount) {
val delta = amount - given // amount lost while given back
if(charger.giveEnergy(delta) < delta) {
NeoComputers.LOGGER.warn("LOSING ENERGY! Tried giving $delta back to $charger and we're losing our marbles!")
}
}
}
}
// only call if storage
fun balanceStorage() {
for(battery in getReachable()) {
if(battery.powerRole != PowerRole.STORAGE) continue
// its so if for example we have a battery with 2x the capacity
// we don't try to even the energy between them since that's just bad
// and might pointless delete energy over time
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
if(meaningfulSurplus <= 0) {
// WE'RE greedy (or negligible surplus)? Do nothing
continue
}
// steal from this greedy mf
val toSteal = meaningfulSurplus / 2
if(toSteal == 0L) continue // broke storahh
val stolen = battery.withdrawEnergy(toSteal)
if(giveEnergy(stolen) < stolen) {
NeoComputers.LOGGER.warn("LOSING ENERGY IN NODE $address!!!! THIS IS REALLY BAD!!!")
}
}
}
// rob the generators
fun stealGeneratorPower() {
var remaining = energyCapacity - energy
for(generator in getReachable()) {
if(generator.powerRole != PowerRole.GENERATOR) continue
// rob this mf
val robbed = generator.withdrawEnergy(remaining)
val taken = giveEnergy(robbed)
if(taken < robbed) {
NeoComputers.LOGGER.warn("energy caught being DELETED in the big 26")
}
remaining -= taken
}
}
open fun tick() {
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
if(powerRole == PowerRole.STORAGE) {
stealGeneratorPower()
balanceStorage()
}
}
// processes a received message
open fun received(message: Message) {}
// called when a new direct connection is made
open fun onConnect(deviceNode: DeviceNode) {}
// called when a direct connection is lost
open fun onDisconnect(deviceNode: DeviceNode) {}
// called when a new node is added globally
open fun onNodeAdded(deviceNode: DeviceNode) {
reachableCache = null;
}
// called when a node is removed globally
open fun onNodeRemoved(deviceNode: DeviceNode) {
reachableCache = null;
}
fun getReachable(): Set<DeviceNode> {
if(reachableCache == null) {
reachableCache = computeReachable();
}
return reachableCache!!;
}
fun invalidateReachableCache() {
reachableCache = null
}
fun computeReachable(): Set<DeviceNode> {
if(reachability == Visibility.NONE) {
return setOf();
}
if(reachability == Visibility.DIRECT) {
return connections.minus(this);
}
if(reachability == Visibility.NETWORK) {
// absolute cinema
val working = HashSet<DeviceNode>();
val pending = mutableListOf(this);
var iterCount = 0;
while(iterCount < maxHopCount && pending.isNotEmpty()) {
iterCount++;
val subnode = pending.removeFirst()
if(subnode in working) continue
working.add(subnode)
if(subnode.reachability == Visibility.NETWORK) {
pending.addAll(subnode.connections)
} else if(subnode.reachability == Visibility.DIRECT) {
working.addAll(subnode.connections)
}
}
// cannot send to itself!
working.remove(this);
return working;
}
throw NotImplementedError("visibility not implemented");
}
fun connectTo(other: DeviceNode) {
this.directConnectTo(other);
other.directConnectTo(this);
}
fun disconnectFrom(other: DeviceNode) {
this.directDisconnectFrom(other);
other.directDisconnectFrom(this);
}
fun directConnectTo(other: DeviceNode) {
if(other == this) return;
if(other in connections) return;
connections.add(other);
this.onConnect(other);
}
fun directDisconnectFrom(other: DeviceNode) {
if(other !in connections) return;
connections.remove(other);
this.onDisconnect(other);
}
// Network synchronization with the NodeSynchronizer
// TODO: process shi
var outOfSync = true
fun markChanged() {
outOfSync = true
}
open fun encodeScreenData(buf: FriendlyByteBuf) {}
// Meant to write the entire state as a single commit
// for clients which say they have no fucking idea what the server is storing
open fun writeFullStateCommit(buf: FriendlyByteBuf) {}
// client-side, meant to bring state forward
open fun processCommit(buf: FriendlyByteBuf) {}
}
abstract class WirelessEndpoint(address: UUID?) : DeviceNode(address) {
abstract fun getRange(): Double
abstract fun getDimension(): Int
abstract fun getPosition(): BlockPos
// separate from process for simplicity
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
}

View File

@@ -1,7 +1,6 @@
package org.neoflock.neocomputers.network package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.MachineEvent import org.neoflock.neocomputers.entity.MachineEvent
import java.util.UUID import java.util.UUID
import kotlin.math.min import kotlin.math.min
@@ -35,226 +34,24 @@ object Networking {
} }
abstract class Message(val sender: Node) abstract class Message(val sender: DeviceNode)
class ClassicPacket(sender: Node, val src: String, val dst: String, val port: Int, val data: List<Any>, val hopCount: Int) : Message(sender) { class ClassicPacket(sender: DeviceNode, val src: String, val dst: String, val port: Int, val data: List<Any>, val hopCount: Int) : Message(sender) {
fun hop() = ClassicPacket(sender, src, dst, port, data, hopCount + 1); fun hop() = ClassicPacket(sender, src, dst, port, data, hopCount + 1);
} }
// for plugins and shi // for plugins and shi
class ComputerCheckedSignal(sender: Node, val player: String?, val name: String, val data: Array<Any>): Message(sender) class ComputerCheckedSignal(sender: DeviceNode, val player: String?, val name: String, val data: Array<Any>): Message(sender)
class ComputerUncheckedSignal(sender: Node, val name: String, val data: Array<Any>): Message(sender) class ComputerUncheckedSignal(sender: DeviceNode, val name: String, val data: Array<Any>): Message(sender)
class ComputerEvent(sender: Node, val machineEvent: MachineEvent): Message(sender) class ComputerEvent(sender: DeviceNode, val machineEvent: MachineEvent): Message(sender)
open class Node(_address: UUID? = null) {
val connections = mutableSetOf<Node>()
private var reachableCache: Set<Node>? = null
var address = _address ?: UUID.randomUUID()
open var reachability = Visibility.NETWORK
open var powerRole = PowerRole.CONSUMER
open var energy: Long = 0
open var energyCapacity: Long = 0
// give energy, returns how much was actually given
// cannot exceed amount specified
open fun giveEnergy(amount: Long): Long {
val maximum = min(amount, energyCapacity - energy)
energy += maximum
return maximum
}
// take energy out, returns how much was actually taken
// cannot exceed amount specified
open fun withdrawEnergy(amount: Long): Long {
val maximum = min(amount, energy)
energy -= maximum
return maximum
}
fun getChargerNodes(): Set<Node> = getReachable().filter { it.powerRole != PowerRole.CONSUMER }.toSet()
fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energy }
fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energyCapacity }
// attempts to consume
fun consumeEnergy(energy: Long): Boolean {
// consumes energy, returns false if not enough
val total = totalEnergyInConnections() + this.energy
if(energy > total) return false
var remaining = energy
remaining -= withdrawEnergy(remaining)
if(remaining <= 0) return true
for (charger in getChargerNodes()) {
if(remaining <= 0) break
remaining -= charger.withdrawEnergy(remaining)
}
return true
}
// PLEASE only call if consumer, in the name of all that is holy
fun tryToChargeFully() {
var remaining = energyCapacity - energy
if(remaining <= 0) return
for (charger in getChargerNodes()) {
if(remaining <= 0) break
val amount = charger.withdrawEnergy(remaining)
val given = giveEnergy(amount)
remaining -= given
if(given < amount) {
val delta = amount - given // amount lost while given back
if(charger.giveEnergy(delta) < delta) {
NeoComputers.LOGGER.warn("LOSING ENERGY! Tried giving $delta back to $charger and we're losing our marbles!")
}
}
}
}
// only call if storage
fun balanceStorage() {
for(battery in getReachable()) {
if(battery.powerRole != PowerRole.STORAGE) continue
// its so if for example we have a battery with 2x the capacity
// we don't try to even the energy between them since that's just bad
// and might pointless delete energy over time
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
if(meaningfulSurplus <= 0) {
// WE'RE greedy (or negligible surplus)? Do nothing
continue
}
// steal from this greedy mf
val toSteal = meaningfulSurplus / 2
if(toSteal == 0L) continue // broke storahh
val stolen = battery.withdrawEnergy(toSteal)
if(giveEnergy(stolen) < stolen) {
NeoComputers.LOGGER.warn("LOSING ENERGY IN NODE $this!!!! THIS IS REALLY BAD!!!")
}
}
}
// rob the generators
fun stealGeneratorPower() {
var remaining = energyCapacity - energy
for(generator in getReachable()) {
if(generator.powerRole != PowerRole.GENERATOR) continue
// rob this mf
val robbed = generator.withdrawEnergy(remaining)
val taken = giveEnergy(robbed)
if(taken < robbed) {
NeoComputers.LOGGER.warn("energy caught being DELETED in the big 26")
}
remaining -= taken
}
}
open fun tick() {
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
if(powerRole == PowerRole.STORAGE) {
stealGeneratorPower()
balanceStorage()
}
}
// processes a received message
open fun received(message: Message) {}
// called when a new direct connection is made
open fun onConnect(node: Node) {}
// called when a direct connection is lost
open fun onDisconnect(node: Node) {}
// called when a new node is added globally
open fun onNodeAdded(node: Node) {
reachableCache = null;
}
// called when a node is removed globally
open fun onNodeRemoved(node: Node) {
reachableCache = null;
}
fun getReachable(): Set<Node> {
if(reachableCache == null) {
reachableCache = computeReachable();
}
return reachableCache!!;
}
fun invalidateReachableCache() {
reachableCache = null
}
fun computeReachable(): Set<Node> {
if(reachability == Visibility.NONE) {
return setOf();
}
if(reachability == Visibility.DIRECT) {
return connections.minus(this);
}
if(reachability == Visibility.NETWORK) {
// absolute cinema
val working = HashSet<Node>();
val pending = mutableListOf(this);
var iterCount = 0;
while(iterCount < maxHopCount && pending.isNotEmpty()) {
iterCount++;
val subnode = pending.removeFirst();
if(subnode in working) continue;
working.add(subnode);
pending.addAll(subnode.connections);
}
// cannot send to itself!
working.remove(this);
return working;
}
throw NotImplementedError("visibility not implemented");
}
fun connectTo(other: Node) {
this.directConnectTo(other);
other.directConnectTo(this);
}
fun disconnectFrom(other: Node) {
this.directDisconnectFrom(other);
other.directDisconnectFrom(this);
}
fun directConnectTo(other: Node) {
if(other == this) return;
if(other in connections) return;
connections.add(other);
this.onConnect(other);
}
fun directDisconnectFrom(other: Node) {
if(other !in connections) return;
connections.remove(other);
this.onDisconnect(other);
}
}
abstract class WirelessEndpoint(address: UUID?) : Node(address) {
abstract fun getRange(): Double
abstract fun getDimension(): Int
abstract fun getPosition(): BlockPos
// separate from process for simplicity
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
}
val wirelessNodes = ThreadLocal.withInitial { HashSet<WirelessEndpoint>() } val wirelessNodes = ThreadLocal.withInitial { HashSet<WirelessEndpoint>() }
val allNodes = ThreadLocal.withInitial { HashMap<UUID, Node>() } val allNodes = ThreadLocal.withInitial { HashMap<UUID, DeviceNode>() }
// 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
fun emitMessage(node: Node, message: Message) { fun emitMessage(deviceNode: DeviceNode, message: Message) {
node.getReachable().forEach { it.received(message) } deviceNode.getReachable().forEach { it.received(message) }
} }
fun computeRangeAllowedByHardness(src: BlockPos, dst: BlockPos): Double { fun computeRangeAllowedByHardness(src: BlockPos, dst: BlockPos): Double {
@@ -285,69 +82,69 @@ object Networking {
tickCount++ tickCount++
} }
fun getNode(address: UUID): Node? = allNodes.get()[address] fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
// TODO: use setter, more convenient // TODO: use setter, more convenient
fun changeNodeAddress(node: Node, address: UUID) { fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) {
if(node.address.equals(address)) return if(deviceNode.address.equals(address)) return
if(node.address !in allNodes.get()) return if(deviceNode.address !in allNodes.get()) return
allNodes.get().remove(node.address) allNodes.get().remove(deviceNode.address)
node.address = address deviceNode.address = address
allNodes.get()[address] = node allNodes.get()[address] = deviceNode
} }
fun addNode(node: Node) { fun addNode(deviceNode: DeviceNode) {
if(node.address in allNodes.get()) return if(deviceNode.address in allNodes.get()) return
allNodes.get()[node.address] = node allNodes.get()[deviceNode.address] = deviceNode
if(node is WirelessEndpoint) { if(deviceNode is WirelessEndpoint) {
wirelessNodes.get().add(node); wirelessNodes.get().add(deviceNode);
} }
// 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.get().forEach { it.value.onNodeAdded(node) } allNodes.get().forEach { it.value.onNodeAdded(deviceNode) }
} }
fun addNodes(vararg nodes: Node) { fun addNodes(vararg deviceNodes: DeviceNode) {
nodes.forEach { addNode(it) } deviceNodes.forEach { addNode(it) }
} }
fun removeNode(node: Node) { fun removeNode(deviceNode: DeviceNode) {
if(node.address !in allNodes.get()) return if(deviceNode.address !in allNodes.get()) return
allNodes.get().forEach { it.value.onNodeRemoved(node) } allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) }
// toList() in order to copy it // toList() in order to copy it
node.connections.toList().forEach { deviceNode.connections.toList().forEach {
node.disconnectFrom(it) deviceNode.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.get().remove(node.address) allNodes.get().remove(deviceNode.address)
if(node is WirelessEndpoint) { if(deviceNode is WirelessEndpoint) {
wirelessNodes.get().remove(node); wirelessNodes.get().remove(deviceNode);
} }
} }
fun removeNodes(vararg nodes: Node) { fun removeNodes(vararg deviceNodes: DeviceNode) {
nodes.forEach { removeNode(it) } deviceNodes.forEach { removeNode(it) }
} }
val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<Node>>() } val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() }
fun addToChannel(channel: String, node: Node) { fun addToChannel(channel: String, deviceNode: DeviceNode) {
val localChannels = channels.get() val localChannels = channels.get()
if(!localChannels.containsKey(channel)) { if(!localChannels.containsKey(channel)) {
localChannels[channel] = mutableSetOf(); localChannels[channel] = mutableSetOf();
} }
localChannels[channel]!!.add(node); localChannels[channel]!!.add(deviceNode);
} }
fun removeFromChannel(channel: String, node: Node) { fun removeFromChannel(channel: String, deviceNode: DeviceNode) {
val localChannels = channels.get() val localChannels = channels.get()
if(!localChannels.containsKey(channel)) return; if(!localChannels.containsKey(channel)) return;
localChannels[channel]?.remove(node); localChannels[channel]?.remove(deviceNode);
if(localChannels[channel].isNullOrEmpty()) { if(localChannels[channel].isNullOrEmpty()) {
localChannels.remove(channel); localChannels.remove(channel);
} }
} }
fun emitChannelMessage(starter: Node, channel: String, message: Message) { fun emitChannelMessage(starter: DeviceNode, channel: String, message: Message) {
val localChannels = channels.get() val localChannels = channels.get()
val c = localChannels[channel] ?: return; val c = localChannels[channel] ?: return;
c.forEach { if(it != starter) it.received(message); }; c.forEach { if(it != starter) it.received(message); };

View File

@@ -15,23 +15,23 @@ object PowerManager {
//? if fabric { //? if fabric {
EnergyStorage.SIDED.registerForBlockEntity({ EnergyStorage.SIDED.registerForBlockEntity({
entity, dir -> object : EnergyStorage { entity, dir -> object : EnergyStorage {
override fun getAmount() = entity.node.energy override fun getAmount() = entity.deviceNode.energy
override fun getCapacity() = entity.node.energyCapacity override fun getCapacity() = entity.deviceNode.energyCapacity
override fun supportsExtraction() = entity.node.powerRole != PowerRole.CONSUMER && entity.node.energyCapacity > 0 override fun supportsExtraction() = entity.deviceNode.powerRole != PowerRole.CONSUMER && entity.deviceNode.energyCapacity > 0
override fun supportsInsertion() = entity.node.powerRole != PowerRole.GENERATOR override fun supportsInsertion() = entity.deviceNode.powerRole != PowerRole.GENERATOR
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long { override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.node.powerRole == PowerRole.CONSUMER) return 0 if(entity.deviceNode.powerRole == PowerRole.CONSUMER) return 0
val taken = entity.node.withdrawEnergy(maxAmount) val taken = entity.deviceNode.withdrawEnergy(maxAmount)
transaction?.addCloseCallback { transaction?.addCloseCallback {
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.node.giveEnergy(taken) ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.deviceNode.giveEnergy(taken)
} }
return taken return taken
} }
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.node.powerRole == PowerRole.GENERATOR) return 0 if(entity.deviceNode.powerRole == PowerRole.GENERATOR) return 0
val given = entity.node.giveEnergy(maxAmount) val given = entity.deviceNode.giveEnergy(maxAmount)
transaction?.addCloseCallback { ctx, res -> transaction?.addCloseCallback { ctx, res ->
if (res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given) if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.withdrawEnergy(given)
} }
return given return given
} }