diff --git a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt index d0485d9..497bb30 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt @@ -1,7 +1,5 @@ package org.neoflock.neocomputers -import com.mojang.blaze3d.vertex.DefaultVertexFormat -import com.mojang.blaze3d.vertex.VertexFormat import dev.architectury.event.events.client.ClientLifecycleEvent import dev.architectury.event.events.common.LifecycleEvent import dev.architectury.event.events.common.PlayerEvent @@ -15,24 +13,20 @@ import org.neoflock.neocomputers.gui.menu.Menus import dev.architectury.utils.Env import dev.architectury.utils.EnvExecutor import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.RenderStateShard -import net.minecraft.client.renderer.RenderType import net.minecraft.server.level.ServerPlayer -import org.neoflock.neocomputers.block.NodeBlockEntity -import org.neoflock.neocomputers.block.NodeSynchronizer -import org.neoflock.neocomputers.gui.buffer.BufferRenderer +import org.neoflock.neocomputers.block.DeviceBlockEntity import org.neoflock.neocomputers.gui.render.ScreenRenderer import org.neoflock.neocomputers.gui.widget.ComponentRoles -import org.neoflock.neocomputers.item.GPUCard import org.neoflock.neocomputers.item.Items import org.neoflock.neocomputers.item.Tabs +import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.Networking +import org.neoflock.neocomputers.network.NodeSynchronizer import org.neoflock.neocomputers.sounds.Sounds import org.neoflock.neocomputers.utils.FontProvider import org.neoflock.neocomputers.utils.GenericContainerScreen import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.nio.Buffer object NeoComputers { const val MODID: String = "neocomputers" @@ -58,7 +52,7 @@ object NeoComputers { Tabs.TABS.register() Sounds.SOUNDS.register() ComponentRoles.mapDefaultTextures() - // i dont know why architectury wants two lambdas but whatever + // I don't know why architectury wants two lambdas but whatever EnvExecutor.runInEnv(Env.CLIENT) {{ ClientLifecycleEvent.CLIENT_SETUP.register { Menus.registerScreens() @@ -87,7 +81,7 @@ object NeoComputers { Networking.channels.remove() } - ClientLifecycleEvent.CLIENT_STARTED.register { + ClientLifecycleEvent.CLIENT_SETUP.register { Networking.allNodes.remove() Networking.wirelessNodes.remove() Networking.channels.remove() @@ -111,19 +105,22 @@ object NeoComputers { packet, ctx -> val player = ctx.player if(player is ServerPlayer) { - NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer) + val ent = NodeSynchronizer.screenMap[player] + if(ent is DeviceNode) { + ent.processScreenInteraction(player, packet.buffer) + } } }) } // we have to do this because the datagen task runs in the physical server EnvExecutor.runInEnv(Env.CLIENT) {{ - NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC, { + NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC, { packet, ctx -> val level = ctx.player.level() val ent = level.getBlockEntity(packet.blockPos) - if(ent is NodeBlockEntity) { - ent.syncWithUpstream(packet.buffer) + if(ent is DeviceBlockEntity) { + ent.processCommits(packet.buffers) } }) @@ -143,7 +140,7 @@ object NeoComputers { }} EnvExecutor.runInEnv(Env.SERVER) {{ // https://github.com/architectury/architectury-api/issues/518 - NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC) + NetworkManager.registerS2CPayloadType(NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC) NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC) NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC) }} diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt index 7958107..4cbf459 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt @@ -27,7 +27,7 @@ import net.minecraft.world.phys.shapes.VoxelShape import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.entity.CableEntity -class CableBlock() : BaseBlock(Properties.of()), EntityBlock { +class CableBlock() : DeviceBlock(Properties.of()), EntityBlock { companion object { val NORTH = BooleanProperty.create("north") val EAST = BooleanProperty.create("east") @@ -96,12 +96,20 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock { fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean { val ent = level.getBlockEntity(npos) val blockState = level.getBlockState(pos) + val theirState = level.getBlockState(npos) - return ent is NodeBlockEntity || (ent is CableEntity && - (level.getBlockState(npos).getValue(COLOR).equals(blockState.getValue(COLOR)) || - blockState.getValue(COLOR).equals(DyeColor.LIGHT_GRAY) || - level.getBlockState(npos).getValue(COLOR).equals(DyeColor.LIGHT_GRAY))) -// val state: BlockState? = (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY)) + val universal = DyeColor.LIGHT_GRAY + if(ent is CableEntity) { + val ourColor = blockState.getValue(COLOR) + val theirColor = theirState.getValue(COLOR) + + if(ourColor.equals(universal)) return true + if(theirColor.equals(universal)) return true + if(ourColor.equals(theirColor)) return true + return false + } + + return ent is DeviceBlockEntity } } @@ -130,9 +138,7 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock { .add(COLOR)) } - override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? { - return CableEntity(pos, state) - } + override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state) override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? { val idx = calcIdx(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN)) diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt b/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt index 58dc9db..eb1a6a9 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt @@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.core.HolderLookup import net.minecraft.nbt.CompoundTag +import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.OutgoingChatMessage import net.minecraft.network.chat.PlayerChatMessage import net.minecraft.server.level.ServerPlayer @@ -25,6 +26,16 @@ open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: Bl val deviceNode = object : DeviceNode() { override var powerRole = PowerRole.STORAGE override var energyCapacity: Long = capacity + + override fun writeFullStateCommit(buf: FriendlyByteBuf) { + super.writeFullStateCommit(buf) + buf.writeVarLong(energy) + } + + override fun processCommit(buf: FriendlyByteBuf) { + super.processCommit(buf) + energy = buf.readVarLong() + } } // TODO: cache list @@ -46,7 +57,7 @@ class CapacitorEntityTier1(pos: BlockPos, state: BlockState): CapacitorEntity(20 class CapacitorEntityTier2(pos: BlockPos, state: BlockState): CapacitorEntity(50000, BlockEntities.CAPACITOR2_ENTITY.get(), pos, state) class CapacitorEntityTier3(pos: BlockPos, state: BlockState): CapacitorEntity(100000, BlockEntities.CAPACITOR3_ENTITY.get(), pos, state) -class CapacitorBlock(val tier: Int) : NodeBlock() { +class CapacitorBlock(val tier: Int) : DeviceBlock() { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity { val cap: CapacitorEntity = when(tier) { 1 -> CapacitorEntityTier1(blockPos, blockState) @@ -54,7 +65,7 @@ class CapacitorBlock(val tier: Int) : NodeBlock() { 3 -> CapacitorEntityTier3(blockPos, blockState) else -> throw UnsupportedOperationException("unsupported tier: $tier") } - return cap.initNetworking() + return cap } override fun useWithoutItem( @@ -64,13 +75,12 @@ class CapacitorBlock(val tier: Int) : NodeBlock() { player: Player, blockHitResult: BlockHitResult ): InteractionResult { - if(!level.isClientSide()) { - val p = player as ServerPlayer + if(level.isClientSide()) { val ent = level.getBlockEntity(blockPos) if(ent is CapacitorEntity) { - if(p.isCrouching) ent.deviceNode.giveEnergy(1) - val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} connected)") - p.sendSystemMessage(OutgoingChatMessage.create(msg).content()) + if(player.isCrouching) ent.deviceNode.giveEnergy(1) + val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} reachable)") + player.sendSystemMessage(OutgoingChatMessage.create(msg).content()) } } return InteractionResult.SUCCESS diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt index 3e22053..fea6399 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt @@ -31,9 +31,10 @@ import org.neoflock.neocomputers.block.CombustionGeneratorBlock.Companion.COMBUS import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.CaseBlockEntity import org.neoflock.neocomputers.entity.MachineEntity +import org.neoflock.neocomputers.network.NodeSynchronizer import org.neoflock.neocomputers.sounds.Sounds -class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff +class CaseBlock() : DeviceBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff companion object { val FACING: EnumProperty = EnumProperty.create("facing", Direction::class.java) val COMPUTER_RUNNING = BooleanProperty.create("running")!! @@ -47,7 +48,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel( registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(COMPUTER_RUNNING, false)) } - override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState).initNetworking() + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState) override fun createBlockStateDefinition(builder: StateDefinition.Builder) { builder.add(COMPUTER_RUNNING) @@ -115,7 +116,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel( } else { // Open menu MenuRegistry.openMenu(player as ServerPlayer, ent) - NodeSynchronizer.registerPlayerScreen(player, ent) + NodeSynchronizer.registerPlayerScreen(player, ent.deviceNode) } } return InteractionResult.SUCCESS diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt index 64a1e92..cb035a2 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt @@ -1,7 +1,11 @@ package org.neoflock.neocomputers.block +import dev.architectury.networking.NetworkManager +import io.netty.buffer.Unpooled import net.minecraft.core.BlockPos import net.minecraft.core.Direction +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.Level import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.EntityBlock @@ -11,9 +15,18 @@ import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.Networking +import org.neoflock.neocomputers.network.NodeSynchronizer + +abstract class SingleDeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): DeviceBlockEntity(type, pos, state) { + abstract val deviceNode: DeviceNode + + override fun getDeviceNodes() = listOf(deviceNode) + override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? = deviceNode +} abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): BlockEntity(type, pos, state) { - val connetionsInDir = MutableList(Direction.entries.size) { HashSet() } + val connetionsInDir = MutableList(Direction.entries.size) { null } + var alreadySetup = false abstract fun getDeviceNodes(): List @@ -22,57 +35,85 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: // so it is Direction.UP if we asked from the one on the top side. abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode? + open fun processCommits(commits: Iterable) { + val devs = getDeviceNodes() + for (buf in commits) { + val idx = buf.readVarInt() + if(idx >= 0 && idx < devs.size) { + devs[idx].processCommit(buf) + } + } + } + open fun initNetworking(): DeviceBlockEntity { - getDeviceNodes().forEach { Networking.addNode(it) } - Direction.entries.forEach { handleConnectionsFor(it) } + if(hasLevel()) { + alreadySetup = true + Networking.addNodes(getDeviceNodes()) + Direction.entries.forEach { handleConnectionsFor(it) } + } return this } - open fun getCurrentlyConnectedNodesIn(direction: Direction): HashSet { + // Cables are 1 node + open fun getCurrentlyConnectedNodeIn(direction: Direction): DeviceNode? { val ent = level?.getBlockEntity(blockPos.relative(direction)) - val connected = HashSet() if(ent is DeviceBlockEntity) { - val node = ent.getNodeFromSide(direction.opposite) - if(node != null) connected.add(node) + return ent.getNodeFromSide(direction.opposite) } - return connected + return null } - // TODO: rethink this shi so sharing a node on 2 different sides doesn't make connections require mutually exclusive conditions - // TODO: actually like, rethink the whole class so far - open fun handleConnectionsFor(direction: Direction) { // refuse connections on no node to reduce CPU load val node = getNodeFromSide(direction.opposite) ?: return val old = connetionsInDir[direction.ordinal] - val now = getCurrentlyConnectedNodesIn(direction) + val now = getCurrentlyConnectedNodeIn(direction) - // TODO: optimize this hellscape - - val toKill = HashSet() - old.forEach { - if(it !in now) toKill.add(it) - } - toKill.forEach { node.disconnectFrom(it) } - now.forEach { - if(it !in old) node.connectTo(it) + if(old?.address != now?.address) { + if(old != null) node.disconnectFrom(old) + if(now != null) node.connectTo(now) } connetionsInDir[direction.ordinal] = now } // TODO: optimize this sometime before our test computers melt - open fun tickDevice() { + open fun tickDevice(level: Level) { // Handles device connections and sync here - // Process connections - Direction.entries.forEach { - handleConnectionsFor(it) + // we do it like this because stinky MC will call stuff before world is fully setup + // and then not notify us of neighbour changes + // this is because MC is considered shit + if(!alreadySetup) { + initNetworking() + } + } + + open fun sendCommitsToClient(level: Level) { + if(level is ServerLevel) { + // synchronization! + val commits = mutableListOf() + val devs = getDeviceNodes() + for((i, dev) in devs.withIndex()) { + // TODO: use dev.outOfSync to only set commits if something changed, and allow client to request the commits (securely) + dev.outOfSync = false + val buf = FriendlyByteBuf(Unpooled.buffer()) + buf.writeVarInt(i) + dev.writeFullStateCommit(buf) + commits.addLast(buf) + } + if(commits.isNotEmpty()) { + level.players().forEach { + val dist = it.position().distanceTo(blockPos.center) + if(dist < 100) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits)) + } + } } } override fun setRemoved() { super.setRemoved() - getDeviceNodes().forEach { Networking.removeNode(it) } + alreadySetup = false + Networking.removeNodes(getDeviceNodes()) } } @@ -85,7 +126,8 @@ abstract class DeviceBlock(properties: Properties = Properties.of()): BaseBlock( return object : BlockEntityTicker { override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T & Any) { if(blockEntity !is DeviceBlockEntity) return - blockEntity.tickDevice() + blockEntity.tickDevice(level) + blockEntity.sendCommitsToClient(level) } } } diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt b/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt index 891e635..74ce600 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/Generators.kt @@ -23,13 +23,14 @@ import net.minecraft.world.phys.BlockHitResult import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity +import org.neoflock.neocomputers.network.NodeSynchronizer -class SolarGeneratorBlock : NodeBlock(), EntityBlock { - override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState).initNetworking() +class SolarGeneratorBlock : DeviceBlock(), EntityBlock { + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState) } // TODO: make it glow when burning -class CombustionGeneratorBlock : NodeBlock, EntityBlock { +class CombustionGeneratorBlock : DeviceBlock, EntityBlock { companion object { val COMBUSTGEN_ACTIVE = BooleanProperty.create("active") @@ -42,7 +43,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock { registerDefaultState(defaultBlockState().setValue(COMBUSTGEN_ACTIVE, false)) } - override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState).initNetworking() + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState) override fun useWithoutItem( blockState: BlockState, @@ -54,7 +55,7 @@ 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) + NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode) MenuRegistry.openMenu(sp, ent) } return InteractionResult.SUCCESS diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt deleted file mode 100644 index 9fe9c89..0000000 --- a/src/main/kotlin/org/neoflock/neocomputers/block/NodeBlock.kt +++ /dev/null @@ -1,304 +0,0 @@ -package org.neoflock.neocomputers.block - -import dev.architectury.networking.NetworkManager -import io.netty.buffer.Unpooled -import net.minecraft.core.BlockPos -import net.minecraft.core.HolderLookup -import net.minecraft.nbt.CompoundTag -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.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.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 -import org.neoflock.neocomputers.network.DeviceNode -import java.time.Duration - -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(NODE_SYNC_ID) - val CODEC = object : StreamCodec { - 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(SCREEN_SYNC_ID) - val CODEC = object : StreamCodec { - 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 - } - - class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload { - companion object { - val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data") - val TYPE = CustomPacketPayload.Type(SCREEN_DATA_ID) - val CODEC = object : StreamCodec { - override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload { - val id = buf.readByteArray().decodeToString() - val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes())) - return ScreenDataPayload(id, buffer) - } - - override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) { - buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray()) - buf.writeBytes(payload.buffer) - } - } - } - - override fun type() = TYPE - } - - class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload { - companion object { - val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data") - val TYPE = CustomPacketPayload.Type(BEEP_DATA_ID) - val CODEC = object : StreamCodec { - override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload { - val pos = buf.readBlockPos() - val pattern = buf.readUtf() - val freq = buf.readVarInt() - val duration = buf.readVarLong() - val volume = buf.readDouble() - return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume) - } - - override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) { - buf.writeBlockPos(payload.pos) - buf.writeUtf(payload.pattern) - buf.writeVarInt(payload.freq) - buf.writeVarLong(payload.duration.toMillis()) - buf.writeDouble(payload.volume) - } - } - } - - override fun type() = TYPE - } - - val screenMap = HashMap() - - 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)) - } - } - - fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) { - NetworkManager.sendToServer(ScreenDataPayload("", friendlyByteBuf)) - } - - fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) { - if(level is ServerLevel) { - level.players().forEach { - NetworkManager.sendToPlayer(it, beepDataPayload) - } - } - } -} - -abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) { - abstract val deviceNode: DeviceNode - - fun initNetworking(): NodeBlockEntity { - 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(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(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 - open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {} - - open fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {} - - private var stateIsDirty = true - - open fun getNeighbourEntities(): List { - val subpos = listOf( - blockPos.offset(0, 0, 1), - blockPos.offset(0, 0, -1), - blockPos.offset(0, 1, 0), - blockPos.offset(0, -1, 0), - blockPos.offset(1, 0, 0), - blockPos.offset(-1, 0, 0), - ) - - return subpos.mapNotNull { pos -> level?.getBlockEntity(pos) } - } - - open fun computeEdges(): Set { - val s = mutableSetOf() - val neighbours = getNeighbourEntities() - for(neighbour in neighbours) { - if(neighbour is NodeBlockEntity) s.add(neighbour); - // TODO: handle cable entities - } - s.remove(this) - return s - } - - open fun invalidateNodeState() { - stateIsDirty = true - } - - open fun needsSynchronization() = stateIsDirty - - 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 { - deviceNode.connectTo(it.deviceNode) - } - } - - override fun setChanged() { - invalidateNodeState() - computeEdges().forEach { it.invalidateNodeState() } - super.setChanged() - } - - override fun setRemoved() { - super.setRemoved() - Networking.removeNode(deviceNode) - } - - override fun clearRemoved() { - super.clearRemoved() - initNetworking() - } - - override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { - super.loadAdditional(compoundTag, provider) - invalidateNodeState() - computeEdges().forEach { it.invalidateNodeState() } - } -} - -abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock { - override fun getTicker( - level: Level, - blockState: BlockState, - blockEntityType: BlockEntityType - ): BlockEntityTicker? { - return object : BlockEntityTicker { - override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) { - if(blockEntity !is NodeBlockEntity) return - if(Networking.getNode(blockEntity.deviceNode.address) == null) blockEntity.initNetworking() - blockEntity.tickNode(level) - } - } - } - - override fun onPlace( - blockState: BlockState, - level: Level, - blockPos: BlockPos, - blockState2: BlockState, - bl: Boolean - ) { - super.onPlace(blockState, level, blockPos, blockState2, bl) - if(!level.isClientSide) { - val ent = level.getBlockEntity(blockPos) - if(ent is NodeBlockEntity) { - ent.invalidateNodeState() - ent.computeEdges().forEach { it.invalidateNodeState() } - } - level.updateNeighborsAt(blockPos, this) - } - } - - override fun neighborChanged( - blockState: BlockState, - level: Level, - blockPos: BlockPos, - block: Block, - blockPos2: BlockPos, - bl: Boolean - ) { - super.neighborChanged(blockState, level, blockPos, block, blockPos2, bl) - if(!level.isClientSide) { - val ent = level.getBlockEntity(blockPos) - if(ent is NodeBlockEntity) { - ent.invalidateNodeState() - } - - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt b/src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt index beb213f..08afcad 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt @@ -15,7 +15,7 @@ import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.DeviceNode -class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) { +class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) { val redstoneIn = Array(Direction.entries.size) {0} val redstoneOut = Array(Direction.entries.size) {0} @@ -44,7 +44,7 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt } } -class RedstoneIOBlock(): NodeBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) { +class RedstoneIOBlock(): DeviceBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = RedstoneIOEntity(blockPos, blockState) fun getRedstoneIO(level: BlockGetter, blockPos: BlockPos): RedstoneIOEntity? { diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/ScreenBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/ScreenBlock.kt index 6a8e2b7..42db7e0 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/ScreenBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/ScreenBlock.kt @@ -26,23 +26,19 @@ import org.neoflock.neocomputers.entity.ScreenEntity import org.neoflock.neocomputers.gui.menu.ScreenMenu import kotlin.math.abs import kotlin.math.max +import org.neoflock.neocomputers.network.NodeSynchronizer -class ScreenBlock() : NodeBlock() { +class ScreenBlock() : DeviceBlock() { companion object { val FACING_HORIZ: EnumProperty = EnumProperty.create("facing_horiz", Direction::class.java) val FACING_VERTI: IntegerProperty = IntegerProperty.create("facing_verti", 0, 2) // "Integer" property doesnt accept values below 0, also death to enums, long live magic numbers - val ENERGY: Long = 5 } init { registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1)) } - override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? { - val scr = ScreenEntity(blockPos, blockState) - scr.initNetworking() - return scr - } + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState) override fun useWithoutItem( blockState: BlockState, @@ -52,14 +48,9 @@ class ScreenBlock() : NodeBlock() { blockHitResult: BlockHitResult ): InteractionResult { if(!level.isClientSide) { - val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get() - if(!screenState.deviceNode.consumeEnergy(ENERGY)) { - player.sendSystemMessage(Component.literal("Not enough power.")) - return InteractionResult.SUCCESS - }; val sp = player as ServerPlayer val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get() - NodeSynchronizer.registerPlayerScreen(sp, ent) + NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode) MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider { override fun getDisplayName(): Component = Component.literal("SCREEEEEN!") override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu { diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt index cd3173d..389fba5 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt @@ -88,12 +88,11 @@ object BlockEntities { } fun registerPowerBlocks() { - // TODO: function for the new DeviceBlockEntity - //PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get()) - //PowerManager.registerPowerBlockEntity(CAPACITOR2_ENTITY.get()) - //PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get()) - PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get()) - PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get()) - PowerManager.registerPowerBlockEntity(CASE_ENTITY.get()) + PowerManager.registerPowerDevice(CAPACITOR_ENTITY.get()) + PowerManager.registerPowerDevice(CAPACITOR2_ENTITY.get()) + PowerManager.registerPowerDevice(CAPACITOR3_ENTITY.get()) + PowerManager.registerPowerDevice(SOLARGEN_ENTITY.get()) + PowerManager.registerPowerDevice(COMBUSTGEN_ENTITY.get()) + PowerManager.registerPowerDevice(CASE_ENTITY.get()) } } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt index 0ace58b..ff804e3 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt @@ -2,15 +2,31 @@ package org.neoflock.neocomputers.entity import net.minecraft.core.BlockPos import net.minecraft.core.Direction +import net.minecraft.network.FriendlyByteBuf import net.minecraft.world.item.DyeColor +import net.minecraft.world.level.Level import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.block.CableBlock import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR import org.neoflock.neocomputers.block.CableBlock.Companion.getPropByDirection -import org.neoflock.neocomputers.block.NodeBlockEntity +import org.neoflock.neocomputers.block.SingleDeviceBlockEntity +import org.neoflock.neocomputers.network.DeviceNode -class CableEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) { +class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) { + override val deviceNode = object : DeviceNode(){} + + override fun sendCommitsToClient(level: Level) { + // we have nothing to commit lol + return + } + + override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? { + if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) { + return deviceNode + } + return null + } override fun setChanged() { super.setChanged() diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt index 7a8f4f4..60d2a80 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt @@ -20,8 +20,7 @@ import net.minecraft.world.level.Level import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.block.CaseBlock -import org.neoflock.neocomputers.block.NodeBlockEntity -import org.neoflock.neocomputers.block.NodeSynchronizer +import org.neoflock.neocomputers.block.SingleDeviceBlockEntity import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.item.ComponentItem import org.neoflock.neocomputers.network.DeviceNode @@ -33,8 +32,10 @@ import org.neoflock.neocomputers.utils.GenericContainer import java.time.Duration import kotlin.math.max import kotlin.math.min +import org.neoflock.neocomputers.network.NodeSynchronizer +import kotlin.text.ifEmpty -class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider { +class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider { val stacks: NonNullList = NonNullList.withSize(7, ItemStack.EMPTY) var isOn = false @@ -47,45 +48,63 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti override val deviceNode = object : DeviceNode() { override var powerRole = PowerRole.CONSUMER override var energyCapacity: Long = 500 - } - override fun encodeDownstreamData(packet: FriendlyByteBuf) { - super.encodeDownstreamData(packet) - packet.writeBoolean(isOn) - packet.writeVarInt(diskActivityTime) - packet.writeVarInt(networkActivityTime) - packet.writeUtf(err ?: "") - } - - override fun syncWithUpstream(packet: FriendlyByteBuf) { - super.syncWithUpstream(packet) - setRunning(packet.readBoolean()) - diskActivityTime = packet.readVarInt() - networkActivityTime = packet.readVarInt() - err = packet.readUtf().ifEmpty { null } - } - - override fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) { - val c = packet.readByte().toInt() - if(c == 0x01) { - start() + override fun writeFullStateCommit(buf: FriendlyByteBuf) { + super.writeFullStateCommit(buf) + buf.writeUUID(address) + buf.writeBoolean(isOn) + buf.writeVarInt(diskActivityTime) + buf.writeVarInt(networkActivityTime) + buf.writeUtf(err ?: "") } - if(c == 0x02) { - stop() - } - } - override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) { - super.encodeScreenData(player, packet) - packet.writeBoolean(isOn) - packet.writeByteArray((err ?: "").encodeToByteArray()) - packet.writeLong(deviceNode.energy) - packet.writeLong(deviceNode.energyCapacity) - packet.writeLong(getMachineMemoryUsed()) - packet.writeLong(getMachineMemoryTotal()) - packet.writeLong(getMachineComponentsUsed()) - packet.writeLong(getMachineComponentsTotal()) - packet.writeUtf(arch) + override fun processCommit(buf: FriendlyByteBuf) { + super.processCommit(buf) + Networking.changeNodeAddress(this, buf.readUUID()) + setRunning(buf.readBoolean()) + diskActivityTime = buf.readVarInt() + networkActivityTime = buf.readVarInt() + err = buf.readUtf().ifEmpty { null } + } + + override fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) { + super.processScreenInteraction(player, buf) + val c = buf.readByte().toInt() + if(c == 0x01) { + start() + } + if(c == 0x02) { + stop() + } + } + + override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) { + super.encodeScreenData(player, buf) + buf.writeBoolean(isOn) + buf.writeByteArray((err ?: "").encodeToByteArray()) + buf.writeLong(energy) + buf.writeLong(energyCapacity) + buf.writeLong(getMachineMemoryUsed()) + buf.writeLong(getMachineMemoryTotal()) + buf.writeLong(getMachineComponentsUsed()) + buf.writeLong(getMachineComponentsTotal()) + buf.writeUtf(arch) + } + + override fun tick() { + super.tick() + if (isRunning()) { + if(diskActivityTime > 0) diskActivityTime-- + if(networkActivityTime > 0) networkActivityTime-- + if(getMachineArchitectures().isEmpty()) { + crash("@neocomputers.errors.ENOCPU") + } else if(getMachineComponentsUsed() > getMachineComponentsTotal()) { + crash("@neocomputers.errors.E2BIG") + } else if (!consumeEnergy(1)) { + crash("@neocomputers.errors.ENOENJ") + } + } + } } val redstoneIn = Array(Direction.entries.size) {0} @@ -154,9 +173,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti override fun start(): Boolean { if(isOn) return true err = null - val archs = getMachineArchitectures() + val architectures = getMachineArchitectures() // Beep patterns taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/server/machine/Machine.scala - if(archs.isEmpty()) { + if(architectures.isEmpty()) { crash("@neocomputers.errors.ENOCPU") beepAsync("-..") return false @@ -178,9 +197,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti beepAsync("-.") return false } - if(arch !in archs) { + if(arch !in architectures) { // Just pick one! TODO: consult EEPROM first - arch = archs.first() + arch = architectures.first() } beepAsync(".") setRunning(true) @@ -194,10 +213,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti } override fun crash(error: String): Boolean { - if(isOn) { - beepAsync("--") - sendMachineEvent(MachineCrashEvent(this, error)) - } + beepAsync("--") + sendMachineEvent(MachineCrashEvent(this, error)) setRunning(false) err = error return true @@ -271,21 +288,4 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti setRunning(false) super.setRemoved() } - - override fun tickNode(level: Level) { - super.tickNode(level) - if(!level.isClientSide) { - if (isRunning()) { - if(diskActivityTime > 0) diskActivityTime-- - if(networkActivityTime > 0) networkActivityTime-- - if(getMachineArchitectures().isEmpty()) { - crash("@neocomputers.errors.ENOCPU") - } else if(getMachineComponentsUsed() > getMachineComponentsTotal()) { - crash("@neocomputers.errors.E2BIG") - } else if (!deviceNode.consumeEnergy(1)) { - crash("@neocomputers.errors.ENOENJ") - } - } - } - } } \ 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 a2f60e6..3545b39 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CombustionGeneratorBlockEntity.kt @@ -1,6 +1,7 @@ package org.neoflock.neocomputers.entity import net.minecraft.core.BlockPos +import net.minecraft.core.Direction import net.minecraft.core.HolderLookup import net.minecraft.core.NonNullList import net.minecraft.nbt.CompoundTag @@ -15,16 +16,15 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.level.Level import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.block.CombustionGeneratorBlock -import org.neoflock.neocomputers.block.NodeBlockEntity +import org.neoflock.neocomputers.block.SingleDeviceBlockEntity 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 import org.neoflock.neocomputers.utils.ContainerUtils import kotlin.math.min -class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider { +class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider { val energyPerTick: Long = 50 var burningTimeRemaining: Int = 0 @@ -32,6 +32,11 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) override val deviceNode = object : DeviceNode() { override var powerRole = PowerRole.GENERATOR override var energyCapacity: Long = 100000 + + override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) { + buf.writeLong(energy) + buf.writeLong(energyCapacity) + } } val stacks: NonNullList = NonNullList.withSize(1, ItemStack.EMPTY) @@ -46,8 +51,8 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) return !this.isRemoved } - override fun tickNode(level: Level) { - super.tickNode(level) + override fun tickDevice(level: Level) { + super.tickDevice(level) // TODO: give us a block state tag for active // keep combusting and shi @@ -79,11 +84,6 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0)) } - override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) { - packet.writeLong(deviceNode.energy) - packet.writeLong(deviceNode.energyCapacity) - } - override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) { super.loadAdditional(compoundTag, provider) deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy")) diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt index e3821c2..72e2536 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt @@ -1,39 +1,67 @@ package org.neoflock.neocomputers.entity; import net.minecraft.core.BlockPos +import net.minecraft.locale.Language import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerPlayer import net.minecraft.world.level.Level import net.minecraft.world.level.block.state.BlockState import org.neoflock.neocomputers.NeoComputers -import org.neoflock.neocomputers.block.NodeBlockEntity +import org.neoflock.neocomputers.block.SingleDeviceBlockEntity import org.neoflock.neocomputers.gui.buffer.BufferRenderer import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.utils.GPUChar import org.neoflock.neocomputers.utils.TextBuffer +import kotlin.text.ifEmpty class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : - NodeBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) { + SingleDeviceBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) { + + var lastError: String? = null + var isOn: Boolean = false override val deviceNode = object : DeviceNode() { override fun received(message: Networking.Message) { super.received(message) if(message is Networking.ComputerEvent) { - // return if not directly connected - if(message.sender !in this.connections) return val mEnv = message.machineEvent NeoComputers.LOGGER.info("Got message $mEnv!") if(mEnv is MachinePowerEvent) { if(mEnv.nowRunning) { - textBuf.set(0, 0, address.toString()) - } else { + lastError = null textBuf.fill(0, 0, textBuf.width, textBuf.height, GPUChar(' ')) + textBuf.set(0, 0, address.toString()) } + isOn = mEnv.nowRunning + } + if(mEnv is MachineCrashEvent) { + lastError = mEnv.error } } } + + override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) { + super.encodeScreenData(player, buf) + textBuf.encodeContents(buf) + } + + override fun writeFullStateCommit(buf: FriendlyByteBuf) { + super.writeFullStateCommit(buf) + buf.writeUUID(address) + buf.writeBoolean(isOn) + buf.writeUtf(lastError ?: "") + textBuf.encodeContents(buf) + } + + override fun processCommit(buf: FriendlyByteBuf) { + super.processCommit(buf) + if(Networking.changeNodeAddress(this, buf.readUUID())) createscreenstuffs() + isOn = buf.readBoolean() + lastError = buf.readUtf().ifEmpty { null } + textBuf.decodeContents(buf) + } } var bound = "screen/unbound" @@ -41,23 +69,8 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : private var cleanrenderer: () -> Unit = { }; // TODO: THIS SUCKS, FIND A BETTER WAY - override fun encodeDownstreamData(packet: FriendlyByteBuf) { - super.encodeDownstreamData(packet) - textBuf.encodeContents(packet) - } - - override fun syncWithUpstream(packet: FriendlyByteBuf) { - super.syncWithUpstream(packet) - textBuf.decodeContents(packet) - } - - override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) { - super.encodeScreenData(player, packet) - textBuf.encodeContents(packet) - } - - override fun tickNode(level: Level) { - super.tickNode(level) + override fun tickDevice(level: Level) { + super.tickDevice(level) cleanrenderer() createscreenstuffs() } @@ -71,9 +84,29 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : private fun createscreenstuffs() { bound = "screen/"+deviceNode.address.toString().replace("-", "_") if (level!!.isClientSide) { - var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf) - renderer.drawBuffer() - cleanrenderer = { renderer.clean() } + if(lastError == null) { + if(!isOn) { + textBuf.fill(0, 0, textBuf.width, textBuf.height) + } + var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf) + renderer.drawBuffer() + cleanrenderer = { renderer.clean() } + } else { + var trueError = lastError!! + if(trueError.startsWith("@")) { + val trans = trueError.substring(1) + val lang = Language.getInstance() + trueError = lang.getOrDefault("neocomputers.computer.errorNoMsg", "Error: ") + lang.getOrDefault(trans) + } + val throwAwayBuf = TextBuffer(50, 16) + val fg = 0xFFFFFF + val bg = 0x2B68A6 + throwAwayBuf.fill(0, 0, throwAwayBuf.width, throwAwayBuf.height, GPUChar(' ', fg, bg)) + throwAwayBuf.set((throwAwayBuf.width - trueError.length) / 2, throwAwayBuf.height/2, trueError, fg, bg) + var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), throwAwayBuf) + renderer.drawBuffer() + cleanrenderer = { renderer.clean() } + } } } } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/SolarGeneratorBlockEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/SolarGeneratorBlockEntity.kt index fdf21b6..15a17bb 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/SolarGeneratorBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/SolarGeneratorBlockEntity.kt @@ -1,16 +1,16 @@ package org.neoflock.neocomputers.entity import net.minecraft.core.BlockPos +import net.minecraft.core.Direction 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.block.SingleDeviceBlockEntity 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) { +class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) { val energyPerTick: Long = 50 override val deviceNode = object : DeviceNode() { @@ -18,8 +18,8 @@ class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : No override var energyCapacity: Long = 50000 } - override fun tickNode(level: Level) { - super.tickNode(level) + override fun tickDevice(level: Level) { + super.tickDevice(level) val l = level if(l.isDay) { deviceNode.giveEnergy(energyPerTick) diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/render/ScreenEntityRenderer.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/render/ScreenEntityRenderer.kt index c029961..5a4e68d 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/render/ScreenEntityRenderer.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/render/ScreenEntityRenderer.kt @@ -27,6 +27,7 @@ class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) : .createCompositeState(false)) } override fun render(entity: ScreenEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) { + if(!entity.isOn && entity.lastError == null) return val facing = entity.blockState.getValue(ScreenBlock.FACING_HORIZ) val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1 diff --git a/src/main/kotlin/org/neoflock/neocomputers/gui/render/BufferRenderer.kt b/src/main/kotlin/org/neoflock/neocomputers/gui/render/BufferRenderer.kt index 0975e74..8a356c6 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/gui/render/BufferRenderer.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/gui/render/BufferRenderer.kt @@ -26,15 +26,11 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB Minecraft.getInstance().textureManager.register(this.id, tex) } - fun dump(path: String) { - image.writeToFile(File(path)) - NeoComputers.LOGGER.info("DUMPED!!!") + fun toRGBA(color: Int): Int { + // Minecaft lies, its AGBR + return java.lang.Integer.reverseBytes((color.toLong() * 256 + 0xFF).toInt()) } -// fun toRGBA(color: Int): Int { -// return color.shl(8).or(0xFF) -// } - fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) { var glyph: ArrayList = FontProvider.map[c]!! @@ -42,7 +38,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB for (i in 0.. 0) image.setPixelRGBA(x+i, y+j, 0xFF000000.toInt()+fg) + if (pixel > 0) image.setPixelRGBA(x+i, y+j, toRGBA(fg)) } } } @@ -53,7 +49,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB var char: GPUChar = buffer.get(i, j) var x = i*CHARW var y = j*CHARH - image.fillRect(x, y, CHARW, CHARH, (0xFF000000+char.bg).toInt()) + image.fillRect(x, y, CHARW, CHARH, toRGBA(char.bg)) if (char.c != ' ' && char.c != '\u0000') drawGlyph(x, y, char.c, char.fg) } } diff --git a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt index 8d7ca0b..02c8ba3 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt @@ -11,7 +11,7 @@ import net.minecraft.world.entity.player.Inventory import net.minecraft.world.inventory.tooltip.TooltipComponent import net.minecraft.world.phys.Vec3 import org.neoflock.neocomputers.NeoComputers -import org.neoflock.neocomputers.block.NodeSynchronizer +import org.neoflock.neocomputers.network.NodeSynchronizer import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.gui.widget.ButtonSprites import org.neoflock.neocomputers.gui.widget.ImagerButton diff --git a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/ScreenScreen.kt b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/ScreenScreen.kt index 31d06df..40c7b5b 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/ScreenScreen.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/ScreenScreen.kt @@ -24,6 +24,8 @@ class ScreenScreen : GenericContainerScreen{ var textBuf = TextBuffer(0, 0) + override fun shouldCenterTitle(): Boolean = false + override fun processScreenStatePacket(buf: FriendlyByteBuf) { super.processScreenStatePacket(buf) textBuf.decodeContents(buf) diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt b/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt index a12a6b1..5a1c1a5 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt @@ -2,6 +2,7 @@ package org.neoflock.neocomputers.network import net.minecraft.core.BlockPos import net.minecraft.network.FriendlyByteBuf +import net.minecraft.server.level.ServerPlayer import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.network.Networking.Message import org.neoflock.neocomputers.network.Networking.Visibility @@ -215,7 +216,8 @@ open class DeviceNode(_address: UUID? = null) { outOfSync = true } - open fun encodeScreenData(buf: FriendlyByteBuf) {} + open fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {} + open fun processScreenInteraction(player: ServerPlayer, 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 diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt index 362113e..79650b6 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt @@ -85,12 +85,14 @@ object Networking { fun getNode(address: UUID): DeviceNode? = allNodes.get()[address] // TODO: use setter, more convenient - fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) { - if(deviceNode.address.equals(address)) return - if(deviceNode.address !in allNodes.get()) return + fun changeNodeAddress(deviceNode: DeviceNode, address: UUID): Boolean { + if(deviceNode.address.equals(address)) return false + if(deviceNode.address !in allNodes.get()) return false + if(address in allNodes.get()) return false allNodes.get().remove(deviceNode.address) deviceNode.address = address allNodes.get()[address] = deviceNode + return true } fun addNode(deviceNode: DeviceNode) { @@ -103,12 +105,17 @@ object Networking { allNodes.get().forEach { it.value.onNodeAdded(deviceNode) } } - fun addNodes(vararg deviceNodes: DeviceNode) { + fun addNodes(deviceNodes: Iterable) { deviceNodes.forEach { addNode(it) } } + fun addNodes(vararg deviceNodes: DeviceNode) { + addNodes(deviceNodes.asIterable()) + } + fun removeNode(deviceNode: DeviceNode) { if(deviceNode.address !in allNodes.get()) return + NodeSynchronizer.nodeErased(deviceNode) allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) } // toList() in order to copy it deviceNode.connections.toList().forEach { @@ -121,10 +128,14 @@ object Networking { } } - fun removeNodes(vararg deviceNodes: DeviceNode) { + fun removeNodes(deviceNodes: Iterable) { deviceNodes.forEach { removeNode(it) } } + fun removeNodes(vararg deviceNodes: DeviceNode) { + removeNodes(deviceNodes.asIterable()) + } + val channels = ThreadLocal.withInitial { HashMap>() } fun addToChannel(channel: String, deviceNode: DeviceNode) { diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt b/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt new file mode 100644 index 0000000..b6daccd --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt @@ -0,0 +1,150 @@ +package org.neoflock.neocomputers.network + +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.level.Level +import org.neoflock.neocomputers.NeoComputers +import java.time.Duration + +object NodeSynchronizer { + class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List): CustomPacketPayload { + companion object { + val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync") + val TYPE = CustomPacketPayload.Type(NODE_SYNC_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload { + val blockPos = buf.readBlockPos() + val bufferCount = buf.readVarInt() + val buffers = List(bufferCount) { + val bytes = buf.readByteArray() + val rawBuf = Unpooled.buffer(bytes.size) + rawBuf.writeBytes(bytes) + FriendlyByteBuf(rawBuf) + } + return DeviceBlockStatePayload(blockPos, buffers) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStatePayload) { + buf.writeBlockPos(payload.blockPos) + buf.writeVarInt(payload.buffers.size) + payload.buffers.forEach { + buf.writeByteArray(it.array()) + } + } + } + } + + override fun type() = TYPE + } + + class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload { + companion object { + val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync") + val TYPE = CustomPacketPayload.Type(SCREEN_SYNC_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload { + val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes())) + return ScreenPayload(buffer) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) { + buf.writeBytes(payload.buffer) + } + } + } + + override fun type() = TYPE + } + + class ScreenDataPayload(var buffer: FriendlyByteBuf): CustomPacketPayload { + companion object { + val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data") + val TYPE = CustomPacketPayload.Type(SCREEN_DATA_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload { + val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes())) + return ScreenDataPayload(buffer) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) { + buf.writeBytes(payload.buffer) + } + } + } + + override fun type() = TYPE + } + + class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload { + companion object { + val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data") + val TYPE = CustomPacketPayload.Type(BEEP_DATA_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload { + val pos = buf.readBlockPos() + val pattern = buf.readUtf() + val freq = buf.readVarInt() + val duration = buf.readVarLong() + val volume = buf.readDouble() + return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) { + buf.writeBlockPos(payload.pos) + buf.writeUtf(payload.pattern) + buf.writeVarInt(payload.freq) + buf.writeVarLong(payload.duration.toMillis()) + buf.writeDouble(payload.volume) + } + } + } + + override fun type() = TYPE + } + + val screenMap = HashMap() + + fun playerScreenClosed(player: ServerPlayer) { + screenMap.remove(player) + } + + fun registerPlayerScreen(player: ServerPlayer, devNode: DeviceNode) { + screenMap[player] = devNode + } + + fun nodeErased(node: DeviceNode) { + var player: ServerPlayer? = null + for((p, n) in screenMap) { + if(n == node) player = p + } + if(player != null) screenMap.remove(player) + } + + fun syncScreens() { + for((player, ent) in screenMap) { + val buf = FriendlyByteBuf(Unpooled.buffer()) + ent.encodeScreenData(player, buf) + NetworkManager.sendToPlayer(player, ScreenPayload(buf)) + } + } + + fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) { + NetworkManager.sendToServer(ScreenDataPayload(friendlyByteBuf)) + } + + fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) { + if(level is ServerLevel) { + level.players().forEach { + NetworkManager.sendToPlayer(it, beepDataPayload) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt b/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt index 2abb8d0..2043929 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/PowerManager.kt @@ -1,9 +1,10 @@ package org.neoflock.neocomputers.network import net.minecraft.world.level.block.entity.BlockEntityType +import org.neoflock.neocomputers.block.DeviceBlockEntity //? if fabric { import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext -import org.neoflock.neocomputers.block.NodeBlockEntity +import net.minecraft.core.Direction import team.reborn.energy.api.EnergyStorage; //?} @@ -11,31 +12,34 @@ import team.reborn.energy.api.EnergyStorage; // the NodeBlockEntity and Node given us a way to get power from a block, we just // need to tell mods how to do it as well object PowerManager { - fun registerPowerBlockEntity(blockEntityType: BlockEntityType) { + fun registerPowerDevice(blockEntityType: BlockEntityType) { //? if fabric { EnergyStorage.SIDED.registerForBlockEntity({ - entity, dir -> object : EnergyStorage { - 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.deviceNode.powerRole == PowerRole.CONSUMER) return 0 - val taken = entity.deviceNode.withdrawEnergy(maxAmount) - transaction?.addCloseCallback { - ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.deviceNode.giveEnergy(taken) + // TODO: as this is currently written, if the node instance changes and the mod cached the conversion, we're boned. Consider fixing it. + entity, dir -> + val node = entity.getNodeFromSide(dir ?: Direction.UP) + if(node == null) null else object : EnergyStorage { + override fun getAmount() = node.energy + override fun getCapacity() = node.energyCapacity + override fun supportsExtraction() = node.powerRole != PowerRole.CONSUMER && node.energyCapacity > 0 + override fun supportsInsertion() = node.powerRole != PowerRole.GENERATOR + override fun extract(maxAmount: Long, transaction: TransactionContext?): Long { + if(node.powerRole == PowerRole.CONSUMER) return 0 + val taken = node.withdrawEnergy(maxAmount) + transaction?.addCloseCallback { + ctx, res -> if(res.wasAborted() || !res.wasCommitted()) node.giveEnergy(taken) + } + return taken } - return taken - } - override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { - if(entity.deviceNode.powerRole == PowerRole.GENERATOR) return 0 - val given = entity.deviceNode.giveEnergy(maxAmount) - transaction?.addCloseCallback { ctx, res -> - if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.withdrawEnergy(given) + override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { + if(node.powerRole == PowerRole.GENERATOR) return 0 + val given = node.giveEnergy(maxAmount) + transaction?.addCloseCallback { ctx, res -> + if (res.wasAborted() || !res.wasCommitted()) node.withdrawEnergy(given) + } + return given } - return given } - } }, blockEntityType); //?} } diff --git a/src/main/resources/assets/neocomputers/textures/item/apu2.png b/src/main/resources/assets/neocomputers/textures/item/apu_creative.png similarity index 100% rename from src/main/resources/assets/neocomputers/textures/item/apu2.png rename to src/main/resources/assets/neocomputers/textures/item/apu_creative.png diff --git a/src/main/resources/assets/neocomputers/textures/item/cbus3.png b/src/main/resources/assets/neocomputers/textures/item/cbus3.png deleted file mode 100644 index 872b3d1..0000000 Binary files a/src/main/resources/assets/neocomputers/textures/item/cbus3.png and /dev/null differ diff --git a/src/main/resources/assets/neocomputers/textures/item/cbus_creative.png b/src/main/resources/assets/neocomputers/textures/item/cbus_creative.png index 8d58a78..872b3d1 100644 Binary files a/src/main/resources/assets/neocomputers/textures/item/cbus_creative.png and b/src/main/resources/assets/neocomputers/textures/item/cbus_creative.png differ diff --git a/src/main/resources/assets/neocomputers/textures/item/server3.png b/src/main/resources/assets/neocomputers/textures/item/server_creative.png similarity index 100% rename from src/main/resources/assets/neocomputers/textures/item/server3.png rename to src/main/resources/assets/neocomputers/textures/item/server_creative.png diff --git a/src/main/resources/assets/neocomputers/textures/item/tabletc3.png b/src/main/resources/assets/neocomputers/textures/item/tabletc_creative.png similarity index 100% rename from src/main/resources/assets/neocomputers/textures/item/tabletc3.png rename to src/main/resources/assets/neocomputers/textures/item/tabletc_creative.png