Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers
This commit is contained in:
@@ -4,38 +4,35 @@ import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.ChatType
|
||||
import net.minecraft.network.chat.OutgoingChatMessage
|
||||
import net.minecraft.network.chat.PlayerChatMessage
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
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 kotlin.math.min
|
||||
|
||||
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 energyCapacity: Long = capacity
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.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) {
|
||||
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 ent = level.getBlockEntity(blockPos)
|
||||
if(ent is CapacitorEntity) {
|
||||
if(p.isCrouching) ent.node.giveEnergy(1)
|
||||
val msg = PlayerChatMessage.system("energy: ${ent.node.energy} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.node.getReachable().size} connected)")
|
||||
if(p.isCrouching) ent.deviceNode.giveEnergy(1)
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import java.time.Duration
|
||||
|
||||
object NodeSynchronizer {
|
||||
@@ -148,30 +149,30 @@ object NodeSynchronizer {
|
||||
}
|
||||
|
||||
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 {
|
||||
Networking.addNode(node)
|
||||
Networking.addNode(deviceNode)
|
||||
invalidateNodeState()
|
||||
return this
|
||||
}
|
||||
|
||||
// runs on the server, meant to encode state to send to all players
|
||||
open fun encodeDownstreamData(packet: FriendlyByteBuf) {
|
||||
packet.writeUUID(node.address)
|
||||
packet.writeLong(node.energy)
|
||||
packet.writeLong(node.energyCapacity)
|
||||
packet.writeEnum(node.reachability)
|
||||
packet.writeEnum(node.powerRole)
|
||||
packet.writeUUID(deviceNode.address)
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
packet.writeEnum(deviceNode.reachability)
|
||||
packet.writeEnum(deviceNode.powerRole)
|
||||
}
|
||||
|
||||
// runs on the client, meant to decode server state packets to synchronize client state
|
||||
open fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||
Networking.changeNodeAddress(node, packet.readUUID())
|
||||
node.energy = packet.readLong()
|
||||
node.energyCapacity = packet.readLong()
|
||||
node.reachability = packet.readEnum(node.reachability.javaClass)
|
||||
node.powerRole = packet.readEnum(node.powerRole.javaClass)
|
||||
Networking.changeNodeAddress(deviceNode, packet.readUUID())
|
||||
deviceNode.energy = packet.readLong()
|
||||
deviceNode.energyCapacity = packet.readLong()
|
||||
deviceNode.reachability = packet.readEnum(deviceNode.reachability.javaClass)
|
||||
deviceNode.powerRole = packet.readEnum(deviceNode.powerRole.javaClass)
|
||||
}
|
||||
|
||||
// Encodes data meant for the associated screen of a player
|
||||
@@ -223,7 +224,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
if(!stateIsDirty) return
|
||||
stateIsDirty = false
|
||||
computeEdges().forEach {
|
||||
node.connectTo(it.node)
|
||||
deviceNode.connectTo(it.deviceNode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +236,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
|
||||
override fun setRemoved() {
|
||||
super.setRemoved()
|
||||
Networking.removeNode(node)
|
||||
Networking.removeNode(deviceNode)
|
||||
}
|
||||
|
||||
override fun clearRemoved() {
|
||||
@@ -259,7 +260,7 @@ abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(pr
|
||||
return object : BlockEntityTicker<T> {
|
||||
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
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.state.BlockState
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
|
||||
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}
|
||||
|
||||
// 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) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,32 +7,23 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
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.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.Level
|
||||
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.state.BlockState
|
||||
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.IntegerProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.entity.ScreenEntity
|
||||
import org.neoflock.neocomputers.gui.menu.Menus
|
||||
import org.neoflock.neocomputers.gui.menu.ScreenMenu
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
@@ -62,7 +53,7 @@ class ScreenBlock() : NodeBlock() {
|
||||
): InteractionResult {
|
||||
if(!level.isClientSide) {
|
||||
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."))
|
||||
return InteractionResult.SUCCESS
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.block.dirToIdx
|
||||
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||
import org.neoflock.neocomputers.item.ComponentItem
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance
|
||||
@@ -44,7 +45,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
var arch = "Lua 5.3"
|
||||
var soundInstance: SoundInstance? = null
|
||||
|
||||
override val node = object : Networking.Node() {
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override var powerRole = PowerRole.CONSUMER
|
||||
override var energyCapacity: Long = 500
|
||||
}
|
||||
@@ -79,8 +80,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
super.encodeScreenData(player, packet)
|
||||
packet.writeBoolean(isOn)
|
||||
packet.writeByteArray((err ?: "").encodeToByteArray())
|
||||
packet.writeLong(node.energy)
|
||||
packet.writeLong(node.energyCapacity)
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
packet.writeLong(getMachineMemoryUsed())
|
||||
packet.writeLong(getMachineMemoryTotal())
|
||||
packet.writeLong(getMachineComponentsUsed())
|
||||
@@ -112,12 +113,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
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) {
|
||||
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")
|
||||
if(oldValue == 0) {
|
||||
// Rising edge
|
||||
@@ -133,7 +134,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
|
||||
fun setRunning(value: Boolean) {
|
||||
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
|
||||
val world = level ?: return
|
||||
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
|
||||
@@ -167,10 +168,10 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
return false
|
||||
}
|
||||
// less than 20% energy is bad
|
||||
if(node.energy < node.energyCapacity/5) {
|
||||
if(deviceNode.energy < deviceNode.energyCapacity/5) {
|
||||
crash("@neocomputers.errors.ENOENJ")
|
||||
// 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
|
||||
}
|
||||
if(getMachineMemoryTotal() == 0L) {
|
||||
@@ -205,7 +206,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
|
||||
override fun getLastError(): String? = err
|
||||
|
||||
override fun getMachineNode(): Networking.Node = node
|
||||
override fun getMachineNode() = deviceNode
|
||||
|
||||
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 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 getMachineArchitecture() = arch
|
||||
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) {
|
||||
super.loadAdditional(compoundTag, provider)
|
||||
node.energy = min(node.energyCapacity, compoundTag.getLong("energy"))
|
||||
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
|
||||
//isOn = compoundTag.getBoolean("powerOn")
|
||||
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.saveAdditional(compoundTag, provider)
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
compoundTag.putLong("energy", deviceNode.energy)
|
||||
//compoundTag.putBoolean("powerOn", isOn)
|
||||
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
|
||||
}
|
||||
@@ -278,11 +279,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
if (isRunning()) {
|
||||
if(diskActivityTime > 0) diskActivityTime--
|
||||
if(networkActivityTime > 0) networkActivityTime--
|
||||
if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
|
||||
crash("too many components")
|
||||
}
|
||||
if (!node.consumeEnergy(1)) {
|
||||
crash("out of energy")
|
||||
if(getMachineArchitectures().isEmpty()) {
|
||||
crash("@neocomputers.errors.ENOCPU")
|
||||
} else if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
|
||||
crash("@neocomputers.errors.E2BIG")
|
||||
} else if (!deviceNode.consumeEnergy(1)) {
|
||||
crash("@neocomputers.errors.ENOENJ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.block.CombustionGeneratorBlock
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
import org.neoflock.neocomputers.utils.GenericContainer
|
||||
@@ -28,7 +29,7 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
|
||||
var burningTimeRemaining: Int = 0
|
||||
|
||||
override val node = object : Networking.Node() {
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override var powerRole = PowerRole.GENERATOR
|
||||
override var energyCapacity: Long = 100000
|
||||
}
|
||||
@@ -52,13 +53,13 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
// keep combusting and shi
|
||||
if(burningTimeRemaining > 0) {
|
||||
burningTimeRemaining--
|
||||
node.giveEnergy(energyPerTick)
|
||||
deviceNode.giveEnergy(energyPerTick)
|
||||
setChanged()
|
||||
return
|
||||
}
|
||||
|
||||
// no point
|
||||
if(node.energy >= node.energyCapacity) return;
|
||||
if(deviceNode.energy >= deviceNode.energyCapacity) return;
|
||||
|
||||
// :fire:
|
||||
val fuel = stacks[0]
|
||||
@@ -79,20 +80,20 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
packet.writeLong(node.energy)
|
||||
packet.writeLong(node.energyCapacity)
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.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")
|
||||
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.saveAdditional(compoundTag, provider)
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
compoundTag.putLong("energy", deviceNode.energy)
|
||||
compoundTag.putInt("burningTimeRemaining", burningTimeRemaining)
|
||||
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.neoflock.neocomputers.item.ComponentItem
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import java.time.Duration
|
||||
|
||||
@@ -39,7 +40,7 @@ interface MachineEntity {
|
||||
fun crash(error: String): Boolean
|
||||
fun getLastError(): String?
|
||||
|
||||
fun getMachineNode(): Networking.Node
|
||||
fun getMachineNode(): DeviceNode
|
||||
|
||||
// Some metadata
|
||||
fun getMachineMemoryTotal(): Long
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.neoflock.neocomputers.entity;
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
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.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
import org.neoflock.neocomputers.utils.GPUChar
|
||||
import org.neoflock.neocomputers.utils.TextBuffer
|
||||
|
||||
class ScreenEntity(blockPos: BlockPos, blockState: 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) {
|
||||
super.received(message)
|
||||
if(message is Networking.ComputerEvent) {
|
||||
@@ -71,8 +69,7 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
}
|
||||
|
||||
private fun createscreenstuffs() {
|
||||
bound = "screen/"+node.address.toString().replace("-", "_")
|
||||
// NeoComputers.LOGGER.info(bound)
|
||||
bound = "screen/"+deviceNode.address.toString().replace("-", "_")
|
||||
if (level!!.isClientSide) {
|
||||
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
|
||||
renderer.drawBuffer()
|
||||
|
||||
@@ -6,13 +6,14 @@ import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
|
||||
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
|
||||
val energyPerTick: Long = 50
|
||||
|
||||
override val node = object : Networking.Node() {
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override var powerRole: PowerRole = PowerRole.GENERATOR
|
||||
override var energyCapacity: Long = 50000
|
||||
}
|
||||
@@ -21,17 +22,17 @@ class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : No
|
||||
super.tickNode(level)
|
||||
val l = level
|
||||
if(l.isDay) {
|
||||
node.giveEnergy(energyPerTick)
|
||||
deviceNode.giveEnergy(energyPerTick)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.loadAdditional(compoundTag, provider)
|
||||
node.energy = compoundTag.getLong("energy")
|
||||
deviceNode.energy = compoundTag.getLong("energy")
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.saveAdditional(compoundTag, provider)
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
compoundTag.putLong("energy", deviceNode.energy)
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
|
||||
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 CBUS1: CBUSItem(2, 12)
|
||||
|
||||
@@ -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 toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
}
|
||||
|
||||
class CPU0: CPUItem(1, 8)
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.neoflock.neocomputers.item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import org.neoflock.neocomputers.entity.MachineEntity
|
||||
import org.neoflock.neocomputers.entity.MachineEvent
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
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
|
||||
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
|
||||
fun getComponentNode(itemStack: ItemStack): Networking.Node? {
|
||||
fun getComponentNode(itemStack: ItemStack): DeviceNode? {
|
||||
val address = itemStack.get(DataComponents.ADDRESS) ?: return null
|
||||
val uuid = UUID.fromString(address) ?: return null
|
||||
return Networking.getNode(uuid)
|
||||
|
||||
@@ -22,7 +22,7 @@ open class DataCard(val tier: Int, val limit: Long): Item(Properties()), Compone
|
||||
}
|
||||
|
||||
// TODO: Modem Component
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
|
||||
override fun appendHoverText(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -32,7 +32,7 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
|
||||
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(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -21,7 +21,7 @@ open class GPUCard(val tier: Int, val vram: Long): Item(Properties()), 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(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -28,7 +28,7 @@ open class HardDiskItem(val tier: Int, val capacity: Long): Item(getDiskProperti
|
||||
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(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -20,7 +20,7 @@ class InternetCard: Item(Item.Properties()), ComponentItem {
|
||||
}
|
||||
|
||||
// TODO: Internet Component
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
|
||||
override fun appendHoverText(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.TooltipFlag
|
||||
import org.neoflock.neocomputers.entity.MachineEntity
|
||||
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
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
|
||||
|
||||
// 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(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -19,7 +19,7 @@ open class NetworkCard(val tier: Int, val maxRange: Int, val isWired: Boolean):
|
||||
}
|
||||
|
||||
// TODO: Modem Component
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
|
||||
override fun appendHoverText(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -21,7 +21,7 @@ open class RedstoneCard(val tier: Int): Item(Properties()), ComponentItem {
|
||||
}
|
||||
|
||||
// TODO: Redstone Component
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
|
||||
override fun appendHoverText(
|
||||
itemStack: ItemStack,
|
||||
|
||||
@@ -20,7 +20,7 @@ class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "cr
|
||||
}
|
||||
|
||||
// TODO: Tunnel Component
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?): Networking.Node? = null
|
||||
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
|
||||
|
||||
override fun appendHoverText(
|
||||
itemStack: ItemStack,
|
||||
|
||||
235
src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt
Normal file
235
src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt
Normal 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) {
|
||||
invalidateReachableCache()
|
||||
}
|
||||
|
||||
// called when a node is removed globally
|
||||
open fun onNodeRemoved(deviceNode: DeviceNode) {
|
||||
invalidateReachableCache()
|
||||
}
|
||||
|
||||
fun getReachable(): Set<DeviceNode> {
|
||||
if(reachableCache == null) {
|
||||
reachableCache = computeReachable();
|
||||
}
|
||||
return reachableCache!!;
|
||||
}
|
||||
|
||||
open 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)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.neoflock.neocomputers.network
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.entity.MachineEvent
|
||||
import java.util.UUID
|
||||
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);
|
||||
}
|
||||
|
||||
// for plugins and shi
|
||||
class ComputerCheckedSignal(sender: Node, 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 ComputerEvent(sender: Node, 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)
|
||||
}
|
||||
class ComputerCheckedSignal(sender: DeviceNode, val player: String?, 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: DeviceNode, val machineEvent: MachineEvent): Message(sender)
|
||||
|
||||
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,
|
||||
// as they might have DIRECT reachability but
|
||||
fun emitMessage(node: Node, message: Message) {
|
||||
node.getReachable().forEach { it.received(message) }
|
||||
fun emitMessage(deviceNode: DeviceNode, message: Message) {
|
||||
deviceNode.getReachable().forEach { it.received(message) }
|
||||
}
|
||||
|
||||
fun computeRangeAllowedByHardness(src: BlockPos, dst: BlockPos): Double {
|
||||
@@ -285,69 +82,69 @@ object Networking {
|
||||
tickCount++
|
||||
}
|
||||
|
||||
fun getNode(address: UUID): Node? = allNodes.get()[address]
|
||||
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
|
||||
|
||||
// TODO: use setter, more convenient
|
||||
fun changeNodeAddress(node: Node, address: UUID) {
|
||||
if(node.address.equals(address)) return
|
||||
if(node.address !in allNodes.get()) return
|
||||
allNodes.get().remove(node.address)
|
||||
node.address = address
|
||||
allNodes.get()[address] = node
|
||||
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) {
|
||||
if(deviceNode.address.equals(address)) return
|
||||
if(deviceNode.address !in allNodes.get()) return
|
||||
allNodes.get().remove(deviceNode.address)
|
||||
deviceNode.address = address
|
||||
allNodes.get()[address] = deviceNode
|
||||
}
|
||||
|
||||
fun addNode(node: Node) {
|
||||
if(node.address in allNodes.get()) return
|
||||
allNodes.get()[node.address] = node
|
||||
if(node is WirelessEndpoint) {
|
||||
wirelessNodes.get().add(node);
|
||||
fun addNode(deviceNode: DeviceNode) {
|
||||
if(deviceNode.address in allNodes.get()) return
|
||||
allNodes.get()[deviceNode.address] = deviceNode
|
||||
if(deviceNode is WirelessEndpoint) {
|
||||
wirelessNodes.get().add(deviceNode);
|
||||
}
|
||||
// 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) {
|
||||
nodes.forEach { addNode(it) }
|
||||
fun addNodes(vararg deviceNodes: DeviceNode) {
|
||||
deviceNodes.forEach { addNode(it) }
|
||||
}
|
||||
|
||||
fun removeNode(node: Node) {
|
||||
if(node.address !in allNodes.get()) return
|
||||
allNodes.get().forEach { it.value.onNodeRemoved(node) }
|
||||
fun removeNode(deviceNode: DeviceNode) {
|
||||
if(deviceNode.address !in allNodes.get()) return
|
||||
allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) }
|
||||
// toList() in order to copy it
|
||||
node.connections.toList().forEach {
|
||||
node.disconnectFrom(it)
|
||||
deviceNode.connections.toList().forEach {
|
||||
deviceNode.disconnectFrom(it)
|
||||
}
|
||||
// actually remove at the end so it can listen to its own removal
|
||||
allNodes.get().remove(node.address)
|
||||
if(node is WirelessEndpoint) {
|
||||
wirelessNodes.get().remove(node);
|
||||
allNodes.get().remove(deviceNode.address)
|
||||
if(deviceNode is WirelessEndpoint) {
|
||||
wirelessNodes.get().remove(deviceNode);
|
||||
}
|
||||
}
|
||||
|
||||
fun removeNodes(vararg nodes: Node) {
|
||||
nodes.forEach { removeNode(it) }
|
||||
fun removeNodes(vararg deviceNodes: DeviceNode) {
|
||||
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()
|
||||
if(!localChannels.containsKey(channel)) {
|
||||
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()
|
||||
if(!localChannels.containsKey(channel)) return;
|
||||
localChannels[channel]?.remove(node);
|
||||
localChannels[channel]?.remove(deviceNode);
|
||||
if(localChannels[channel].isNullOrEmpty()) {
|
||||
localChannels.remove(channel);
|
||||
}
|
||||
}
|
||||
|
||||
fun emitChannelMessage(starter: Node, channel: String, message: Message) {
|
||||
fun emitChannelMessage(starter: DeviceNode, channel: String, message: Message) {
|
||||
val localChannels = channels.get()
|
||||
val c = localChannels[channel] ?: return;
|
||||
c.forEach { if(it != starter) it.received(message); };
|
||||
|
||||
@@ -15,23 +15,23 @@ object PowerManager {
|
||||
//? if fabric {
|
||||
EnergyStorage.SIDED.registerForBlockEntity({
|
||||
entity, dir -> object : EnergyStorage {
|
||||
override fun getAmount() = entity.node.energy
|
||||
override fun getCapacity() = entity.node.energyCapacity
|
||||
override fun supportsExtraction() = entity.node.powerRole != PowerRole.CONSUMER && entity.node.energyCapacity > 0
|
||||
override fun supportsInsertion() = entity.node.powerRole != PowerRole.GENERATOR
|
||||
override fun getAmount() = entity.deviceNode.energy
|
||||
override fun getCapacity() = entity.deviceNode.energyCapacity
|
||||
override fun supportsExtraction() = entity.deviceNode.powerRole != PowerRole.CONSUMER && entity.deviceNode.energyCapacity > 0
|
||||
override fun supportsInsertion() = entity.deviceNode.powerRole != PowerRole.GENERATOR
|
||||
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.node.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = entity.node.withdrawEnergy(maxAmount)
|
||||
if(entity.deviceNode.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = entity.deviceNode.withdrawEnergy(maxAmount)
|
||||
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
|
||||
}
|
||||
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.node.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = entity.node.giveEnergy(maxAmount)
|
||||
if(entity.deviceNode.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = entity.deviceNode.giveEnergy(maxAmount)
|
||||
transaction?.addCloseCallback { ctx, res ->
|
||||
if (res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given)
|
||||
if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.withdrawEnergy(given)
|
||||
}
|
||||
return given
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user