diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt b/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt index f7f8ae1..ae6f187 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt @@ -2,15 +2,11 @@ package org.neoflock.neocomputers.block import net.minecraft.core.BlockPos import net.minecraft.network.chat.ChatType -import net.minecraft.network.chat.Component 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.item.ItemStack -import net.minecraft.world.item.TooltipFlag import net.minecraft.world.level.Level import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState @@ -25,7 +21,7 @@ class CapacitorEntity(pos: BlockPos, state: BlockState) : NodeBlockEntity(BlockE val capacity: Long = 20000 override val node = object : Networking.Node() { - override fun getPowerRole() = PowerRole.PRODUCER + override fun getPowerRole() = PowerRole.STORAGE override fun getEnergy() = amountStored override fun getEnergyCapacity() = capacity override fun giveEnergy(amount: Long): Long { diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt b/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt index 7e91cf3..1be25c6 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt @@ -1,12 +1,23 @@ package org.neoflock.neocomputers.block +import net.minecraft.client.resources.sounds.Sound 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.ServerPlayer +import net.minecraft.world.InteractionResult +import net.minecraft.world.entity.player.Player 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.FurnaceBlock import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntityTicker import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockBehaviour 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.SolarGeneratorBlockEntity import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity @@ -30,7 +41,8 @@ class SolarGeneratorBlock : BaseBlock(), EntityBlock { } } -class CombustionGeneratorBlock : BaseBlock(), EntityBlock { +// TODO: make it glow when burning +class CombustionGeneratorBlock : Block(BlockBehaviour.Properties.of()), EntityBlock { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity { return CombustionGeneratorBlockEntity(blockPos, blockState) } @@ -43,8 +55,28 @@ class CombustionGeneratorBlock : BaseBlock(), EntityBlock { return object : BlockEntityTicker { override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) { if(blockEntity !is CombustionGeneratorBlockEntity) return; - blockEntity.giveSolarPower(); + blockEntity.burnFuelForEnergy(); } } } + + 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.COMBUSTGEN_ENTITY.get()) + if(ent.isPresent) { + val bust = ent.get() + val fuel = bust.stacks[0] + val msg = PlayerChatMessage.system("${fuel.displayName.string} x ${fuel.count} (${bust.node.getEnergy()} / ${bust.node.getEnergyCapacity()} J)") + sp.sendChatMessage(OutgoingChatMessage.create(msg), false, ChatType.bind(ChatType.CHAT, player)) + } + } + return InteractionResult.SUCCESS; + } } diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt index af89181..e735c53 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt @@ -60,5 +60,6 @@ object BlockEntities { fun registerPowerBlocks() { PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get()) + PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get()) } } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt index f92fb95..1c80f59 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt @@ -1,20 +1,72 @@ 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.core.NonNullList +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.ItemStack import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.block.NodeBlockEntity +import org.neoflock.neocomputers.network.Networking +import org.neoflock.neocomputers.network.PowerRole +import org.neoflock.neocomputers.utils.GenericContainer +import org.neoflock.neocomputers.utils.ContainerUtils +import kotlin.math.min -class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : BlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState) { +class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer { val energyPerTick: Long = 50 - fun giveSolarPower() { - if(level?.isDay == true) { - val below = level?.getBlockEntity(blockPos.below()) - if(below is NodeBlockEntity) { - below.node.giveEnergy(energyPerTick) - } + var energy: Long = 0 + val maxEnergy: Long = 50000 + var burningTimeRemaining: Int = 0 + + override val node = object : Networking.Node() { + override fun getPowerRole() = PowerRole.GENERATOR + override fun getEnergy() = energy + override fun getEnergyCapacity() = maxEnergy + override fun withdrawEnergy(amount: Long): Long { + val taken = min(amount, energy) + energy -= taken + return taken + } + + override fun giveEnergy(amount: Long): Long { + val given = min(amount, maxEnergy - energy) + energy += given + return given } } + + val stacks: NonNullList = NonNullList.withSize(1, ItemStack.EMPTY) + + override fun canPlaceItem(i: Int, itemStack: ItemStack): Boolean { + return ContainerUtils.isBurningFuel(itemStack) + } + + override fun getItems(): NonNullList = stacks + + override fun stillValid(player: Player): Boolean { + return !this.isRemoved + } + + fun burnFuelForEnergy() { + // TODO: give us a block state tag for active + + // keep combusting and shi + if(burningTimeRemaining > 0) { + burningTimeRemaining-- + node.giveEnergy(energyPerTick) + setChanged() + return + } + + // no point + if(node.getEnergy() >= node.getEnergyCapacity()) return; + + // :fire: + val fuel = stacks[0] + if(fuel.isEmpty) return + + burningTimeRemaining = ContainerUtils.getBurningTime(fuel) ?: 0 + fuel.count-- + } } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt index 5de912c..2743026 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt @@ -2,7 +2,6 @@ package org.neoflock.neocomputers.network import net.minecraft.core.BlockPos import org.neoflock.neocomputers.NeoComputers -import java.lang.ref.WeakReference import kotlin.math.min import kotlin.math.pow import kotlin.math.sqrt @@ -11,9 +10,12 @@ enum class PowerRole { // consumes energy, wants to be fully charged // does not give energy to network nodes CONSUMER, - // produces/stores energy, will not care to charge itself + // stores energy, will not care to charge itself // will happily give energy to network nodes - PRODUCER, + STORAGE, + // only produces energy, thus obviously charges itself + // also happily gives energy + GENERATOR, } object Networking { @@ -56,7 +58,7 @@ object Networking { open fun withdrawEnergy(amount: Long): Long = 0 open fun getEnergyCapacity(): Long = 0 - fun getChargerNodes(): Set = getReachable().filter { it.getPowerRole() == PowerRole.PRODUCER }.toSet() + fun getChargerNodes(): Set = getReachable().filter { it.getPowerRole() != PowerRole.CONSUMER }.toSet() fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.getEnergy() } fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.getEnergyCapacity() } @@ -78,6 +80,7 @@ object Networking { return true } + // PLEASE only call if consumer, in the name of all that is holy fun tryToChargeFully() { var remaining = getEnergyCapacity() - getEnergy() if(remaining <= 0) return @@ -95,8 +98,55 @@ object Networking { } } + // only call if storage + fun balanceStorage() { + for(battery in getReachable()) { + if(battery.getPowerRole() != 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 = getEnergyCapacity().toDouble() / battery.getEnergyCapacity() + + val meaningfulSurplus = (battery.getEnergy() * capacityRatio - getEnergy()).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 = getEnergyCapacity() - getEnergy() + + for(generator in getReachable()) { + if(generator.getPowerRole() != 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(getPowerRole() == PowerRole.CONSUMER) tryToChargeFully() + if(getPowerRole() == PowerRole.STORAGE) { + stealGeneratorPower() + balanceStorage() + } } // processes a received message open fun received(message: Message) {} diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt b/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt index dbe74bd..99baa6b 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt @@ -17,9 +17,10 @@ object PowerManager { entity, dir -> object : EnergyStorage { override fun getAmount() = entity.node.getEnergy() override fun getCapacity() = entity.node.getEnergyCapacity() - override fun supportsExtraction() = entity.node.getPowerRole() == PowerRole.PRODUCER - override fun supportsInsertion(): Boolean = entity.node.getEnergyCapacity() > 0 + override fun supportsExtraction() = entity.node.getPowerRole() != PowerRole.CONSUMER + override fun supportsInsertion() = entity.node.getPowerRole() != PowerRole.GENERATOR override fun extract(maxAmount: Long, transaction: TransactionContext?): Long { + if(entity.node.getPowerRole() == PowerRole.CONSUMER) return 0 val taken = entity.node.withdrawEnergy(maxAmount) transaction?.addCloseCallback { ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.node.giveEnergy(taken) @@ -27,9 +28,10 @@ object PowerManager { return taken } override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { + if(entity.node.getPowerRole() == PowerRole.GENERATOR) return 0 val given = entity.node.giveEnergy(maxAmount) - transaction?.addCloseCallback { - ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given) + transaction?.addCloseCallback { ctx, res -> + if (res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given) } return given } diff --git a/src/main/kotlin/org/neoflock/neocomputers/utils/ContainerUtils.kt b/src/main/kotlin/org/neoflock/neocomputers/utils/ContainerUtils.kt new file mode 100644 index 0000000..892ba92 --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/utils/ContainerUtils.kt @@ -0,0 +1,15 @@ +package org.neoflock.neocomputers.utils + +import dev.architectury.registry.fuel.FuelRegistry +import net.minecraft.world.item.ItemStack + +// mewhenthe, aka e, will have me publicly executed for this code +object ContainerUtils { + fun getBurningTime(itemStack: ItemStack): Int? { + val time = FuelRegistry.get(itemStack) + if(time == 0) return null + return time + } + + fun isBurningFuel(itemStack: ItemStack) = getBurningTime(itemStack) != null +} \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/utils/GenericContainer.kt b/src/main/kotlin/org/neoflock/neocomputers/utils/GenericContainer.kt new file mode 100644 index 0000000..06ed98c --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/utils/GenericContainer.kt @@ -0,0 +1,48 @@ +package org.neoflock.neocomputers.utils + +// based off the ImplementedContainer of https://docs.fabricmc.net/develop/blocks/block-containers +import net.minecraft.world.Container; +import net.minecraft.core.NonNullList; +import net.minecraft.world.ContainerHelper +import net.minecraft.world.item.ItemStack + +// Common container interface, assumes the entire purpose is purely raw item storage +interface GenericContainer : Container { + fun getItems(): NonNullList + + override fun getContainerSize(): Int { + return getItems().size + } + + override fun isEmpty(): Boolean { + return getItems().all { it.isEmpty } + } + + override fun getItem(i: Int): ItemStack { + return getItems()[i] + } + + override fun removeItem(slot: Int, count: Int): ItemStack { + val res = ContainerHelper.removeItem(getItems(), slot, count) + if (!res.isEmpty) setChanged() + return res + } + + override fun setItem(slot: Int, itemStack: ItemStack) { + getItems()[slot] = itemStack + + // in case of bullshit + if(itemStack.count > itemStack.maxStackSize) { + // rip items + itemStack.count = itemStack.maxStackSize + } + } + + override fun removeItemNoUpdate(i: Int): ItemStack { + return ContainerHelper.takeItem(getItems(), i) + } + + override fun clearContent() { + getItems().clear() + } +} \ No newline at end of file