synchronization and persistant state
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package org.neoflock.neocomputers
|
||||
|
||||
import dev.architectury.event.events.client.ClientLifecycleEvent
|
||||
import dev.architectury.event.events.common.PlayerEvent
|
||||
import dev.architectury.event.events.common.TickEvent
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.neoflock.neocomputers.block.Blocks
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
@@ -10,10 +12,16 @@ import org.neoflock.neocomputers.gui.menu.Menus
|
||||
import org.neoflock.neocomputers.gui.screen.ScreenScreen
|
||||
import dev.architectury.registry.menu.MenuRegistry
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import org.neoflock.neocomputers.block.NodeBlock
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.item.Items
|
||||
import org.neoflock.neocomputers.item.Tabs
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.utils.FontProvider
|
||||
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
@@ -54,12 +62,40 @@ object NeoComputers {
|
||||
|
||||
TickEvent.SERVER_POST.register {
|
||||
Networking.tickAllNodes()
|
||||
NodeSynchronizer.syncScreens()
|
||||
}
|
||||
|
||||
PlayerEvent.CLOSE_MENU.register {
|
||||
player, menu ->
|
||||
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
|
||||
}
|
||||
|
||||
PlayerEvent.PLAYER_QUIT.register {
|
||||
player ->
|
||||
NodeSynchronizer.playerScreenClosed(player)
|
||||
}
|
||||
|
||||
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC, {
|
||||
packet, ctx ->
|
||||
val level = ctx.player.level()
|
||||
val ent = level.getBlockEntity(packet.blockPos)
|
||||
if(ent is NodeBlockEntity) {
|
||||
ent.syncWithUpstream(packet.buffer)
|
||||
}
|
||||
})
|
||||
|
||||
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC, {
|
||||
packet, ctx ->
|
||||
val scr = Minecraft.getInstance().screen
|
||||
if(scr is GenericContainerScreen<*>) {
|
||||
scr.processScreenStatePacket(packet.buffer)
|
||||
}
|
||||
})
|
||||
|
||||
LOGGER.info("Registered!")
|
||||
//LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader()))
|
||||
//LOGGER.info("Kotlin: %s".formatted(NeoComputers.hello()))
|
||||
LOGGER.info("Started mod in ${NeoComputers.PLATFORM?.modloader} loader")
|
||||
LOGGER.info("Started mod in ${PLATFORM?.modloader} loader")
|
||||
LOGGER.info("Hello from kotlin!")
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import org.neoflock.neocomputers.NeoComputers
|
||||
import java.util.function.Supplier
|
||||
import com.google.common.base.Suppliers
|
||||
|
||||
open class BaseBlock : Block(BlockBehaviour.Properties.of()) { // TODO: create a TieredBaseBlock class that extends this or something
|
||||
open class BaseBlock(properties: Properties = Properties.of()) : Block(properties) { // TODO: create a TieredBaseBlock class that extends this or something
|
||||
// val tier: Int
|
||||
|
||||
companion object Registry {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
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
|
||||
@@ -19,23 +22,18 @@ 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) {
|
||||
var amountStored: Long = 0
|
||||
|
||||
override val node = object : Networking.Node() {
|
||||
override fun getPowerRole() = PowerRole.STORAGE
|
||||
override fun getEnergy() = amountStored
|
||||
override fun getEnergyCapacity() = capacity
|
||||
override fun giveEnergy(amount: Long): Long {
|
||||
val given = min(amount, capacity - amountStored)
|
||||
amountStored += given
|
||||
return given
|
||||
}
|
||||
override var powerRole = PowerRole.STORAGE
|
||||
override var energyCapacity: Long = capacity
|
||||
}
|
||||
|
||||
override fun withdrawEnergy(amount: Long): Long {
|
||||
val taken = min(amount, amountStored)
|
||||
amountStored -= taken
|
||||
return taken
|
||||
}
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
node.energy = min(compoundTag.getLong("energy"), node.energyCapacity)
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,15 +59,15 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if(!level.isClientSide()) {
|
||||
val sp = player as ServerPlayer
|
||||
if(level.isClientSide()) {
|
||||
val p = player as LocalPlayer
|
||||
val ent = level.getBlockEntity(blockPos)
|
||||
if(ent is CapacitorEntity) {
|
||||
if(sp.isCrouching) ent.amountStored++
|
||||
val msg = PlayerChatMessage.system("energy: ${ent.amountStored} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.node.getReachable().size} connected)")
|
||||
sp.sendChatMessage(OutgoingChatMessage.create(msg), false, ChatType.bind(ChatType.CHAT, player))
|
||||
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)")
|
||||
p.sendSystemMessage(OutgoingChatMessage.create(msg).content())
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,30 @@ package org.neoflock.neocomputers.block
|
||||
|
||||
import dev.architectury.registry.menu.MenuRegistry
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
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.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
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.EntityBlock
|
||||
import net.minecraft.world.level.block.FurnaceBlock
|
||||
import net.minecraft.world.level.block.SoundType
|
||||
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.storage.loot.LootParams
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity
|
||||
@@ -22,7 +36,19 @@ class SolarGeneratorBlock : NodeBlock(), EntityBlock {
|
||||
}
|
||||
|
||||
// TODO: make it glow when burning
|
||||
class CombustionGeneratorBlock : NodeBlock(), EntityBlock {
|
||||
class CombustionGeneratorBlock : NodeBlock, EntityBlock {
|
||||
companion object {
|
||||
val ACTIVE = BooleanProperty.create("active")
|
||||
|
||||
fun getLuminance(blockState: BlockState): Int {
|
||||
return if(blockState.getValue(ACTIVE)) 5 else 0
|
||||
}
|
||||
}
|
||||
|
||||
constructor(): super(Properties.of().sound(SoundType.STONE).lightLevel(CombustionGeneratorBlock::getLuminance)) {
|
||||
registerDefaultState(defaultBlockState().setValue(ACTIVE, false))
|
||||
}
|
||||
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState).initNetworking()
|
||||
|
||||
override fun useWithoutItem(
|
||||
@@ -35,8 +61,25 @@ class CombustionGeneratorBlock : NodeBlock(), EntityBlock {
|
||||
if(!level.isClientSide()) {
|
||||
val sp = player as ServerPlayer
|
||||
val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get()
|
||||
NodeSynchronizer.registerPlayerScreen(sp, ent)
|
||||
MenuRegistry.openMenu(sp, ent)
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(ACTIVE)
|
||||
}
|
||||
|
||||
override fun animateTick(blockState: BlockState, level: Level, blockPos: BlockPos, randomSource: RandomSource) {
|
||||
if(blockState.getValue(ACTIVE)) {
|
||||
if(randomSource.nextDouble() < 0.1) level.playSound(null, blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT)
|
||||
|
||||
val x = blockPos.x.toDouble()
|
||||
val y = blockPos.y.toDouble()
|
||||
val z = blockPos.z.toDouble()
|
||||
|
||||
level.addParticle(ParticleTypes.SMOKE, x+0.5, y+0.5, z+0.5, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -11,8 +20,73 @@ 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.BlockState
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
|
||||
object NodeSynchronizer {
|
||||
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync")
|
||||
val TYPE = CustomPacketPayload.Type<StatePayload>(NODE_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, StatePayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): StatePayload {
|
||||
val blockPos = buf.readBlockPos()
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return StatePayload(blockPos, buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: StatePayload) {
|
||||
buf.writeBlockPos(payload.blockPos)
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
|
||||
val id = buf.readByteArray().decodeToString()
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenPayload(id, buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
|
||||
buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray())
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
val screenMap = mutableMapOf<ServerPlayer, NodeBlockEntity>()
|
||||
|
||||
fun playerScreenClosed(player: ServerPlayer) {
|
||||
screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun nodeTypeToWireID(nodeType: BlockEntityType<*>): String = nodeType.javaClass.canonicalName
|
||||
|
||||
fun registerPlayerScreen(player: ServerPlayer, entity: NodeBlockEntity) {
|
||||
screenMap[player] = entity
|
||||
}
|
||||
|
||||
fun syncScreens() {
|
||||
for((player, ent) in screenMap) {
|
||||
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||
ent.encodeScreenData(player, buf)
|
||||
NetworkManager.sendToPlayer(player, ScreenPayload(nodeTypeToWireID(ent.type), buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
|
||||
abstract val node: Networking.Node
|
||||
|
||||
@@ -21,6 +95,27 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
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)
|
||||
}
|
||||
|
||||
// runs on the client, meant to decode server state packets to synchronize client state
|
||||
open fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||
node.address = packet.readUUID()
|
||||
node.energy = packet.readLong()
|
||||
node.energyCapacity = packet.readLong()
|
||||
node.reachability = packet.readEnum(node.reachability.javaClass)
|
||||
node.powerRole = packet.readEnum(node.powerRole.javaClass)
|
||||
}
|
||||
|
||||
// Encodes data meant for the associated screen of a player
|
||||
open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {}
|
||||
|
||||
private var stateIsDirty = true
|
||||
|
||||
open fun getNeighbourEntities(): List<BlockEntity> {
|
||||
@@ -36,7 +131,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
return subpos.mapNotNull { pos -> level?.getBlockEntity(pos) }
|
||||
}
|
||||
|
||||
fun computeEdges(): Set<NodeBlockEntity> {
|
||||
open fun computeEdges(): Set<NodeBlockEntity> {
|
||||
val s = mutableSetOf<NodeBlockEntity>()
|
||||
val neighbours = getNeighbourEntities()
|
||||
for(neighbour in neighbours) {
|
||||
@@ -47,13 +142,21 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
return s
|
||||
}
|
||||
|
||||
fun invalidateNodeState() {
|
||||
open fun invalidateNodeState() {
|
||||
stateIsDirty = true
|
||||
}
|
||||
|
||||
fun needsSynchronization() = stateIsDirty
|
||||
|
||||
open fun tickNode() {
|
||||
open fun tickNode(level: Level) {
|
||||
if(!level.isClientSide) {
|
||||
val l = level as ServerLevel
|
||||
val packetBuf = FriendlyByteBuf(Unpooled.buffer())
|
||||
encodeDownstreamData(packetBuf)
|
||||
l.players().forEach {
|
||||
if(it.level().isLoaded(blockPos)) NetworkManager.sendToPlayer(it, NodeSynchronizer.StatePayload(blockPos, packetBuf))
|
||||
}
|
||||
}
|
||||
if(!stateIsDirty) return
|
||||
stateIsDirty = false
|
||||
computeEdges().forEach {
|
||||
@@ -73,7 +176,7 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NodeBlock: BaseBlock(), EntityBlock {
|
||||
abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock {
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
level: Level,
|
||||
blockState: BlockState,
|
||||
@@ -82,7 +185,7 @@ abstract class NodeBlock: BaseBlock(), EntityBlock {
|
||||
return object : BlockEntityTicker<T> {
|
||||
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) {
|
||||
if(blockEntity !is NodeBlockEntity) return;
|
||||
blockEntity.tickNode()
|
||||
blockEntity.tickNode(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,32 +40,32 @@ class BullshitFix: DataFixType<Unit>() {
|
||||
object BlockEntities {
|
||||
val BLOCKENTITIES: DeferredRegister<BlockEntityType<*>> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK_ENTITY_TYPE);
|
||||
|
||||
val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen_entity") {
|
||||
val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen") {
|
||||
BlockEntityType(
|
||||
::ScreenEntity, mutableSetOf(Blocks.SCREEN_BLOCK.get()), BullshitFix()
|
||||
)
|
||||
}
|
||||
val CAPACITOR_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier1>> = BLOCKENTITIES.register("capacitor_entity") {
|
||||
val CAPACITOR_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier1>> = BLOCKENTITIES.register("capacitor") {
|
||||
BlockEntityType(
|
||||
::CapacitorEntityTier1, mutableSetOf(Blocks.CAPACITOR_BLOCK.get()), BullshitFix()
|
||||
)
|
||||
}
|
||||
val CAPACITOR2_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier2>> = BLOCKENTITIES.register("capacitor_entity2") {
|
||||
val CAPACITOR2_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier2>> = BLOCKENTITIES.register("capacitor2") {
|
||||
BlockEntityType(
|
||||
::CapacitorEntityTier2, mutableSetOf(Blocks.CAPACITOR_BLOCK2.get()), BullshitFix()
|
||||
)
|
||||
}
|
||||
val CAPACITOR3_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier3>> = BLOCKENTITIES.register("capacitor_entity3") {
|
||||
val CAPACITOR3_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier3>> = BLOCKENTITIES.register("capacitor3") {
|
||||
BlockEntityType(
|
||||
::CapacitorEntityTier3, mutableSetOf(Blocks.CAPACITOR_BLOCK3.get()), BullshitFix()
|
||||
)
|
||||
}
|
||||
val SOLARGEN_ENTITY: RegistrySupplier<BlockEntityType<SolarGeneratorBlockEntity>> = BLOCKENTITIES.register("solargen_entity") {
|
||||
val SOLARGEN_ENTITY: RegistrySupplier<BlockEntityType<SolarGeneratorBlockEntity>> = BLOCKENTITIES.register("solargen") {
|
||||
BlockEntityType(
|
||||
::SolarGeneratorBlockEntity, mutableSetOf(Blocks.SOLARGEN_BLOCK.get()), BullshitFix()
|
||||
)
|
||||
}
|
||||
val COMBUSTGEN_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("combustgen_entity") {
|
||||
val COMBUSTGEN_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("combustgen") {
|
||||
BlockEntityType(
|
||||
::CombustionGeneratorBlockEntity, mutableSetOf(Blocks.COMBUSTGEN_BLOCK.get()), BullshitFix()
|
||||
)
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
package org.neoflock.neocomputers.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.MenuProvider
|
||||
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.level.Level
|
||||
import net.minecraft.world.level.block.FurnaceBlock
|
||||
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.Networking
|
||||
@@ -20,25 +30,11 @@ import kotlin.math.min
|
||||
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
|
||||
val energyPerTick: Long = 50
|
||||
|
||||
var energy: Long = 0
|
||||
val maxEnergy: Long = 100000
|
||||
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
|
||||
}
|
||||
override var powerRole = PowerRole.GENERATOR
|
||||
override var energyCapacity: Long = 100000
|
||||
}
|
||||
|
||||
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(1, ItemStack.EMPTY)
|
||||
@@ -53,8 +49,8 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
return !this.isRemoved
|
||||
}
|
||||
|
||||
override fun tickNode() {
|
||||
super.tickNode()
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
// TODO: give us a block state tag for active
|
||||
|
||||
// keep combusting and shi
|
||||
@@ -66,17 +62,38 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
}
|
||||
|
||||
// no point
|
||||
if(node.getEnergy() >= node.getEnergyCapacity()) return;
|
||||
if(node.energy >= node.energyCapacity) return;
|
||||
|
||||
// :fire:
|
||||
val fuel = stacks[0]
|
||||
if(fuel.isEmpty) return
|
||||
|
||||
burningTimeRemaining = ContainerUtils.getBurningTime(fuel) ?: 0
|
||||
setChanged()
|
||||
fuel.count--
|
||||
}
|
||||
|
||||
override fun getDisplayName(): Component? = Component.translatable("block.neocomputers.combustgen")
|
||||
|
||||
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CombustionGeneratorMenu(i, inventory, this)
|
||||
|
||||
override fun setChanged() {
|
||||
super.setChanged()
|
||||
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.ACTIVE, burningTimeRemaining > 0))
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
packet.writeLong(node.energy)
|
||||
packet.writeLong(node.energyCapacity)
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
node.energy = min(node.energyCapacity, compoundTag.getLong("energy"))
|
||||
burningTimeRemaining = compoundTag.getInt("burningTimeRemaining")
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
compoundTag.putInt("burningTimeRemaining", burningTimeRemaining)
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,35 @@
|
||||
package org.neoflock.neocomputers.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
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.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
import kotlin.math.min
|
||||
|
||||
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
|
||||
val energyPerTick: Long = 50
|
||||
var energyStored: Long = 0
|
||||
val capacity: Long = 50000
|
||||
|
||||
override val node = object : Networking.Node() {
|
||||
override fun getPowerRole(): PowerRole = PowerRole.GENERATOR
|
||||
override fun getEnergy(): Long = energyStored
|
||||
override fun getEnergyCapacity(): Long = capacity
|
||||
override fun giveEnergy(amount: Long): Long {
|
||||
val taken = min(amount, capacity - energyStored)
|
||||
energyStored += taken
|
||||
return taken
|
||||
}
|
||||
override fun withdrawEnergy(amount: Long): Long {
|
||||
val taken = min(amount, energyStored)
|
||||
energyStored -= taken
|
||||
return taken
|
||||
}
|
||||
override var powerRole: PowerRole = PowerRole.GENERATOR
|
||||
override var energyCapacity: Long = 50000
|
||||
}
|
||||
|
||||
override fun tickNode() {
|
||||
super.tickNode()
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
val l = level ?: return
|
||||
if(l.isDay) {
|
||||
node.giveEnergy(energyPerTick)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
node.energy = compoundTag.getLong("energy")
|
||||
}
|
||||
|
||||
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
compoundTag.putLong("energy", node.energy)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
package org.neoflock.neocomputers.gui.screen
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
|
||||
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||
|
||||
class CombustionGeneratorScreen(abstractContainerMenu: CombustionGeneratorMenu, inventory: Inventory, component: Component) : GenericContainerScreen<CombustionGeneratorMenu>(abstractContainerMenu, inventory, component) {
|
||||
override fun findMenuTexture(): ResourceLocation = ResourceLocation.withDefaultNamespace("textures/gui/container/dispenser.png")
|
||||
|
||||
var energy: Long = 0
|
||||
var energyCapacity: Long = 1
|
||||
|
||||
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
|
||||
super.render(graphics, mouseX, mouseY, something)
|
||||
|
||||
@@ -21,9 +26,16 @@ class CombustionGeneratorScreen(abstractContainerMenu: CombustionGeneratorMenu,
|
||||
val lineY = imageY + 6
|
||||
val lineHeight = 60
|
||||
|
||||
val power = 0.2
|
||||
val power = energy.toDouble() / energyCapacity
|
||||
|
||||
graphics.fill(lineX, lineY, lineX + 2, lineY + lineHeight, lineFg)
|
||||
graphics.fill(lineX, lineY, lineX + 2, lineY + (lineHeight * (1.0 - power)).toInt(), lineBg)
|
||||
}
|
||||
|
||||
override fun getBoundBlockEntityType() = setOf(BlockEntities.COMBUSTGEN_ENTITY.get())
|
||||
|
||||
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||
energy = buf.readLong()
|
||||
energyCapacity = buf.readLong()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.neoflock.neocomputers.network
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import java.util.UUID
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
@@ -46,26 +47,35 @@ object Networking {
|
||||
open class Node {
|
||||
val connections = mutableSetOf<Node>()
|
||||
private var reachableCache: Set<Node>? = null
|
||||
open var address = UUID.randomUUID()
|
||||
|
||||
open fun getReachability() = Visibility.NETWORK
|
||||
open fun getPowerRole() = PowerRole.CONSUMER
|
||||
open fun getEnergy(): Long = 0
|
||||
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 = 0
|
||||
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 = 0
|
||||
open fun withdrawEnergy(amount: Long): Long {
|
||||
val maximum = min(amount, energy)
|
||||
energy -= maximum
|
||||
return maximum
|
||||
}
|
||||
|
||||
open fun getEnergyCapacity(): Long = 0
|
||||
fun getChargerNodes(): Set<Node> = 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() }
|
||||
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() + getEnergy()
|
||||
val total = totalEnergyInConnections() + this.energy
|
||||
if(energy > total) return false
|
||||
|
||||
var remaining = energy
|
||||
@@ -82,7 +92,7 @@ object Networking {
|
||||
|
||||
// PLEASE only call if consumer, in the name of all that is holy
|
||||
fun tryToChargeFully() {
|
||||
var remaining = getEnergyCapacity() - getEnergy()
|
||||
var remaining = energyCapacity - energy
|
||||
if(remaining <= 0) return
|
||||
for (charger in getChargerNodes()) {
|
||||
if(remaining <= 0) break
|
||||
@@ -101,13 +111,13 @@ object Networking {
|
||||
// only call if storage
|
||||
fun balanceStorage() {
|
||||
for(battery in getReachable()) {
|
||||
if(battery.getPowerRole() != PowerRole.STORAGE) continue
|
||||
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 = getEnergyCapacity().toDouble() / battery.getEnergyCapacity()
|
||||
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
|
||||
|
||||
val meaningfulSurplus = (battery.getEnergy() * capacityRatio - getEnergy()).toLong()
|
||||
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
|
||||
|
||||
if(meaningfulSurplus <= 0) {
|
||||
// WE'RE greedy (or negligible surplus)? Do nothing
|
||||
@@ -127,10 +137,10 @@ object Networking {
|
||||
|
||||
// rob the generators
|
||||
fun stealGeneratorPower() {
|
||||
var remaining = getEnergyCapacity() - getEnergy()
|
||||
var remaining = energyCapacity - energy
|
||||
|
||||
for(generator in getReachable()) {
|
||||
if(generator.getPowerRole() != PowerRole.GENERATOR) continue
|
||||
if(generator.powerRole != PowerRole.GENERATOR) continue
|
||||
// rob this mf
|
||||
val robbed = generator.withdrawEnergy(remaining)
|
||||
val taken = giveEnergy(robbed)
|
||||
@@ -142,8 +152,8 @@ object Networking {
|
||||
}
|
||||
|
||||
open fun tick() {
|
||||
if(getPowerRole() == PowerRole.CONSUMER) tryToChargeFully()
|
||||
if(getPowerRole() == PowerRole.STORAGE) {
|
||||
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
|
||||
if(powerRole == PowerRole.STORAGE) {
|
||||
stealGeneratorPower()
|
||||
balanceStorage()
|
||||
}
|
||||
@@ -178,7 +188,6 @@ object Networking {
|
||||
}
|
||||
|
||||
fun computeReachable(): Set<Node> {
|
||||
val reachability = getReachability()
|
||||
if(reachability == Visibility.NONE) {
|
||||
return setOf();
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ object PowerManager {
|
||||
//? if fabric {
|
||||
EnergyStorage.SIDED.registerForBlockEntity({
|
||||
entity, dir -> object : EnergyStorage {
|
||||
override fun getAmount() = entity.node.getEnergy()
|
||||
override fun getCapacity() = entity.node.getEnergyCapacity()
|
||||
override fun supportsExtraction() = entity.node.getPowerRole() != PowerRole.CONSUMER
|
||||
override fun supportsInsertion() = entity.node.getPowerRole() != PowerRole.GENERATOR
|
||||
override fun getAmount() = entity.node.energy
|
||||
override fun getCapacity() = entity.node.energyCapacity
|
||||
override fun supportsExtraction() = entity.node.powerRole != PowerRole.CONSUMER
|
||||
override fun supportsInsertion() = entity.node.powerRole != PowerRole.GENERATOR
|
||||
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.node.getPowerRole() == PowerRole.CONSUMER) return 0
|
||||
if(entity.node.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = entity.node.withdrawEnergy(maxAmount)
|
||||
transaction?.addCloseCallback {
|
||||
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.node.giveEnergy(taken)
|
||||
@@ -28,7 +28,7 @@ object PowerManager {
|
||||
return taken
|
||||
}
|
||||
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.node.getPowerRole() == PowerRole.GENERATOR) return 0
|
||||
if(entity.node.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = entity.node.giveEnergy(maxAmount)
|
||||
transaction?.addCloseCallback { ctx, res ->
|
||||
if (res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given)
|
||||
|
||||
@@ -5,6 +5,8 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.Resource
|
||||
@@ -15,6 +17,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
|
||||
// Common container interface, assumes the entire purpose is purely raw item storage
|
||||
interface GenericContainer : Container {
|
||||
@@ -112,6 +115,10 @@ abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventor
|
||||
open fun shouldRenderTooltip() = true
|
||||
open fun findMenuTexture(): ResourceLocation? = null
|
||||
|
||||
open fun getBoundBlockEntityType(): Set<BlockEntityType<*>> = setOf()
|
||||
|
||||
open fun processScreenStatePacket(buf: FriendlyByteBuf) {}
|
||||
|
||||
val imageX: Int
|
||||
get() = (width - imageWidth) / 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user