power bullshit

This commit is contained in:
2026-04-11 21:48:45 +02:00
parent f5b5a30299
commit 71b6d3a606
8 changed files with 213 additions and 39 deletions

View File

@@ -3,8 +3,10 @@ package org.neoflock.neocomputers
import com.google.common.base.Suppliers import com.google.common.base.Suppliers
import dev.architectury.event.events.client.ClientLifecycleEvent import dev.architectury.event.events.client.ClientLifecycleEvent
import dev.architectury.event.events.common.LifecycleEvent import dev.architectury.event.events.common.LifecycleEvent
import dev.architectury.event.events.common.TickEvent
import dev.architectury.registry.client.gui.MenuScreenRegistry import dev.architectury.registry.client.gui.MenuScreenRegistry
import dev.architectury.registry.registries.RegistrarManager import dev.architectury.registry.registries.RegistrarManager
import net.minecraft.util.profiling.jfr.event.ServerTickTimeEvent
import org.neoflock.neocomputers.block.Blocks import org.neoflock.neocomputers.block.Blocks
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.gui.menu.Menus import org.neoflock.neocomputers.gui.menu.Menus
@@ -37,24 +39,9 @@ object NeoComputers {
MenuScreenRegistry.registerScreenFactory(Menus.SCREEN_MENU.get(), ::ScreenScreen) MenuScreenRegistry.registerScreenFactory(Menus.SCREEN_MENU.get(), ::ScreenScreen)
} }
val logA = Networking.LoggerNode("LogA") TickEvent.SERVER_POST.register {
val logB = Networking.LoggerNode("LogB") Networking.tickAllNodes()
val batteryA = Networking.DebugBatteryNode(0.0, 10000.0) }
val batteryB = Networking.DebugBatteryNode(15000.0, 20000.0)
logA.connectTo(logB)
logA.connectTo(batteryA)
logB.connectTo(batteryB)
Networking.addNodes(logA, logB, batteryA, batteryB)
Networking.emitMessage(logA, Networking.ClassicPacket(logA, "a", "b", 0, listOf(), 0))
LOGGER.info("A: ${batteryA.getEnergy()} / ${batteryA.maxEnergyCapacity()}, B: ${batteryB.getEnergy()} / ${batteryB.maxEnergyCapacity()}")
Networking.tickAllNodes();
LOGGER.info("A: ${batteryA.getEnergy()} / ${batteryA.maxEnergyCapacity()}, B: ${batteryB.getEnergy()} / ${batteryB.maxEnergyCapacity()}")
LOGGER.info("Had enough: ${if(logA.consumeEnergy(600.0)) 'Y' else 'N'}")
LOGGER.info("A: ${batteryA.getEnergy()} / ${batteryA.maxEnergyCapacity()}, B: ${batteryB.getEnergy()} / ${batteryB.maxEnergyCapacity()}")
Networking.removeNodes(logA, logB, batteryA, batteryB)
LOGGER.info("Registered!") LOGGER.info("Registered!")
//LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader())) //LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader()))

View File

@@ -27,6 +27,7 @@ object Blocks {
val BLOCKS: DeferredRegister<Block> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK) val BLOCKS: DeferredRegister<Block> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK)
val TEST_BLOCK: RegistrySupplier<Block> = BaseBlock.register("test") { BaseBlock("test") } val TEST_BLOCK: RegistrySupplier<Block> = BaseBlock.register("test") { BaseBlock("test") }
val SCREEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("screen") { ScreenBlock() } val SCREEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("screen") { ScreenBlock() }
val CAPACITOR_BLOCK: RegistrySupplier<Block> = BaseBlock.register("capacitor") { CapacitorBlock() }
fun registerBlockItems() { fun registerBlockItems() {
BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block> -> BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block> ->

View File

@@ -0,0 +1,67 @@
package org.neoflock.neocomputers.block
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.ChatType
import net.minecraft.network.chat.OutgoingChatMessage
import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.RandomSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.EntityBlock
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.entity.NodeEntity
import org.neoflock.neocomputers.network.Networking
class CapacitorEntity(pos: BlockPos, state: BlockState) : NodeEntity(BlockEntities.CAPACITOR_ENTITY.get(), pos, state) {
var amountStored: Double = 0.0
val capacity = 20000.0
val netNode = object : Networking.Node() {
override fun isProducer() = true
override fun getEnergy() = amountStored
override fun maxEnergyCapacity(): Double = capacity
override fun setEnergy(energy: Double) {
amountStored = energy
}
}
override fun getNode() = netNode
}
class CapacitorBlock : BaseBlock("capacitor"), EntityBlock {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? {
val cap = CapacitorEntity(blockPos, blockState)
cap.syncReachable()
Networking.addNode(cap.getNode())
return cap
}
override fun useWithoutItem(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if(!level.isClientSide()) {
val sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.CAPACITOR_ENTITY.get())
if(ent.isPresent()) {
val cap = ent.get()
if(sp.isCrouching()) cap.amountStored++
val msg = PlayerChatMessage.system("energy: ${cap.amountStored} / ${cap.capacity} (${cap.getReachableNodes().size} reachable, ${cap.getNode().getReachable().size} connected)")
sp.sendChatMessage(OutgoingChatMessage.create(msg), false, ChatType.bind(ChatType.CHAT, player))
}
}
return InteractionResult.SUCCESS;
}
}

View File

@@ -17,13 +17,17 @@ 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.ScreenEntity import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.Menus import org.neoflock.neocomputers.gui.menu.Menus
import org.neoflock.neocomputers.network.Networking
class ScreenBlock() : BaseBlock("screen"), EntityBlock { class ScreenBlock() : BaseBlock("screen"), EntityBlock {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? {
return ScreenEntity(blockPos, blockState) val scr = ScreenEntity(blockPos, blockState)
Networking.addNode(scr.getNode())
return scr
} }
override fun useWithoutItem( override fun useWithoutItem(
@@ -34,6 +38,8 @@ class ScreenBlock() : BaseBlock("screen"), EntityBlock {
blockHitResult: BlockHitResult blockHitResult: BlockHitResult
): InteractionResult { ): InteractionResult {
if(!level.isClientSide) { if(!level.isClientSide) {
val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
if(!screenState.getNode().consumeEnergy(5.0)) return InteractionResult.SUCCESS;
MenuRegistry.openMenu(player as ServerPlayer, object : MenuProvider { MenuRegistry.openMenu(player as ServerPlayer, object : MenuProvider {
override fun getDisplayName(): Component = Component.literal("SCREEEEEN!") override fun getDisplayName(): Component = Component.literal("SCREEEEEN!")
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu { override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {

View File

@@ -6,9 +6,11 @@ import net.minecraft.core.registries.Registries
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.Blocks import org.neoflock.neocomputers.block.Blocks
import org.neoflock.neocomputers.block.CapacitorEntity
object BlockEntities { object BlockEntities {
val BLOCKENTITIES: DeferredRegister<BlockEntityType<*>> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK_ENTITY_TYPE); val BLOCKENTITIES: DeferredRegister<BlockEntityType<*>> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK_ENTITY_TYPE);
val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen_entity") { BlockEntityType(::ScreenEntity, mutableSetOf(Blocks.SCREEN_BLOCK.get()))} val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen_entity") { BlockEntityType(::ScreenEntity, mutableSetOf(Blocks.SCREEN_BLOCK.get()))}
val CAPACITOR_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntity>> = BLOCKENTITIES.register("capacitor_entity") { BlockEntityType(::CapacitorEntity, mutableSetOf(Blocks.CAPACITOR_BLOCK.get()))}
} }

View File

@@ -0,0 +1,85 @@
package org.neoflock.neocomputers.entity;
import net.minecraft.core.BlockPos
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 org.neoflock.neocomputers.network.Networking
open class NodeEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) :
BlockEntity(blockEntityType, blockPos, blockState) {
// stuff
open fun getNode(): Networking.Node? = null
open fun getDirectConnections(): List<NodeEntity> {
if(level == null) return listOf();
val offs = listOf(
BlockPos(0, 1, 0),
BlockPos(0, -1, 0),
BlockPos(1, 0, 0),
BlockPos(-1, 0, 0),
BlockPos(0, 0, 1),
BlockPos(0, 0, -1),
)
val entities = mutableListOf<NodeEntity>()
offs.forEach {
val ent = level?.getBlockEntity(blockPos.offset(it.x, it.y, it.z))
if(ent is NodeEntity) {
entities.add(ent)
}
}
return entities
}
// may include itself
fun getReachableNodes(): Set<Networking.Node> {
val visited = mutableSetOf<NodeEntity>()
val working = mutableListOf<NodeEntity>(this)
val nodes = mutableSetOf<Networking.Node>()
while(working.isNotEmpty()) {
val cur = working.removeFirst()
if(cur in visited) continue
visited.add(cur)
val n = cur.getNode()
if(n != null) {
// rely on the defined direct connections of the node
nodes.add(n)
if(n != this.getNode()) continue
}
working.addAll(cur.getDirectConnections());
}
return nodes
}
fun syncReachable() {
val reachable = getReachableNodes().toList()
val node = getNode()
// nothing to sync
if(node == null) return
reachable.filter {
it !in node.connections
}.forEach {
node.connectTo(it)
}
node.connections.filter { it !in reachable }.forEach {
node.disconnectFrom(it)
}
}
override fun setChanged() {
super.setChanged()
syncReachable()
}
override fun setRemoved() {
super.setRemoved()
syncReachable()
val n = getNode()
if(n != null) Networking.removeNode(n);
}
}

View File

@@ -4,9 +4,22 @@ import net.minecraft.core.BlockPos
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 org.neoflock.neocomputers.network.Networking
class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
BlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) { NodeEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
var energyStored: Double = 0.0
val scrnod = object : Networking.Node() {
override fun getEnergy() = energyStored
override fun setEnergy(energy: Double) {
energyStored = energy
}
override fun maxEnergyCapacity(): Double = 10.0
override fun isConsumer() = true
}
// stuff // stuff
override fun getNode() = scrnod
} }

View File

@@ -36,45 +36,53 @@ object Networking {
val reachability = Visibility.NETWORK val reachability = Visibility.NETWORK
var reachableCache: Set<Node>? = null var reachableCache: Set<Node>? = null
open fun isProducer(): Boolean = false
open fun isConsumer(): Boolean = false
open fun getEnergy(): Double = 0.0 open fun getEnergy(): Double = 0.0
open fun setEnergy(energy: Double) {} open fun setEnergy(energy: Double) {}
open fun maxEnergyCapacity(): Double = 0.0 open fun maxEnergyCapacity(): Double = 0.0
open fun getChargerNodes(): Set<Node> = getReachable().plus(this) fun getChargerNodes(): Set<Node> = getReachable().filter { it.isProducer() }.toSet()
fun totalEnergyInConnections(): Double = getChargerNodes().fold(0.0) { acc, node -> acc + node.getEnergy() } fun totalEnergyInConnections(): Double = getChargerNodes().fold(0.0) { acc, node -> acc + node.getEnergy() }
fun maxEnergyInConnections(): Double = getChargerNodes().fold(0.0) { acc, node -> acc + node.maxEnergyCapacity() } fun maxEnergyInConnections(): Double = getChargerNodes().fold(0.0) { acc, node -> acc + node.maxEnergyCapacity() }
// the algorithm for balancing energy levels fun consumeFromNodeAsMuchAsPossible(energy: Double): Double {
fun balanceEnergyLevels() { val consumed = min(energy, getEnergy())
// basic algorithm: ensure equal percentages setEnergy(getEnergy() - consumed)
val cap = this.maxEnergyInConnections(); return consumed
val total = this.totalEnergyInConnections();
val percentage = total / cap;
getChargerNodes().forEach {
it.setEnergy(percentage * it.maxEnergyCapacity());
}
} }
// attempts to consume // attempts to consume
fun consumeEnergy(energy: Double): Boolean { fun consumeEnergy(energy: Double): Boolean {
// consumes energy, returns false if not enough // consumes energy, returns false if not enough
val total = this.totalEnergyInConnections() val total = totalEnergyInConnections() + getEnergy()
if(energy > total) return false if(energy > total) return false
val percentageConsumed = energy / total var remaining = energy
remaining -= consumeFromNodeAsMuchAsPossible(remaining)
if(remaining <= 0.0) return true
getChargerNodes().forEach { for (charger in getChargerNodes()) {
it.setEnergy(it.getEnergy() * (1.0 - percentageConsumed)); if(remaining <= 0.0) break
remaining -= charger.consumeFromNodeAsMuchAsPossible(remaining)
} }
return true return true
} }
fun tryToChargeFully() {
var remaining = maxEnergyCapacity() - getEnergy()
if(remaining <= 0.0) return
for (charger in getChargerNodes()) {
if(remaining <= 0.0) break
val amount = charger.consumeFromNodeAsMuchAsPossible(remaining)
setEnergy(getEnergy() + amount)
remaining -= amount
}
}
open fun tick() { open fun tick() {
// rationale: the other ones can figure it out if(isConsumer()) tryToChargeFully()
if(this.maxEnergyCapacity() > 0) this.balanceEnergyLevels()
} }
// processes a received message // processes a received message
open fun received(message: Message) {} open fun received(message: Message) {}
@@ -132,7 +140,7 @@ object Networking {
other.directConnectTo(this); other.directConnectTo(this);
} }
fun disconnectTo(other: Node) { fun disconnectFrom(other: Node) {
this.directDisconnectFrom(other); this.directDisconnectFrom(other);
other.directDisconnectFrom(this); other.directDisconnectFrom(this);
} }
@@ -158,6 +166,7 @@ object Networking {
} }
class DebugBatteryNode(var power: Double, val capacity: Double): Node() { class DebugBatteryNode(var power: Double, val capacity: Double): Node() {
override fun isProducer() = true
override fun maxEnergyCapacity() = capacity override fun maxEnergyCapacity() = capacity
override fun getEnergy() = power override fun getEnergy() = power
override fun setEnergy(energy: Double) { power = energy } override fun setEnergy(energy: Double) { power = energy }
@@ -230,6 +239,10 @@ object Networking {
if(node is WirelessEndpoint) { if(node is WirelessEndpoint) {
wirelessNodes.remove(node); wirelessNodes.remove(node);
} }
// toList() in order to copy it
node.connections.toList().forEach {
node.disconnectFrom(it)
}
allNodes.forEach { it.onNodeRemoved(node) } allNodes.forEach { it.onNodeRemoved(node) }
} }