NodeBlockEntity is dead, long live SingleDeviceBlockEntity + cables function

This commit is contained in:
2026-04-28 17:16:33 +03:00
parent 464584877c
commit 43255b1caf
28 changed files with 495 additions and 537 deletions

View File

@@ -1,7 +1,5 @@
package org.neoflock.neocomputers 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.client.ClientLifecycleEvent
import dev.architectury.event.events.common.LifecycleEvent import dev.architectury.event.events.common.LifecycleEvent
import dev.architectury.event.events.common.PlayerEvent 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.Env
import dev.architectury.utils.EnvExecutor import dev.architectury.utils.EnvExecutor
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.DeviceBlockEntity
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
import org.neoflock.neocomputers.gui.render.ScreenRenderer import org.neoflock.neocomputers.gui.render.ScreenRenderer
import org.neoflock.neocomputers.gui.widget.ComponentRoles import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.item.GPUCard
import org.neoflock.neocomputers.item.Items import org.neoflock.neocomputers.item.Items
import org.neoflock.neocomputers.item.Tabs import org.neoflock.neocomputers.item.Tabs
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.sounds.Sounds import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.FontProvider import org.neoflock.neocomputers.utils.FontProvider
import org.neoflock.neocomputers.utils.GenericContainerScreen import org.neoflock.neocomputers.utils.GenericContainerScreen
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.nio.Buffer
object NeoComputers { object NeoComputers {
const val MODID: String = "neocomputers" const val MODID: String = "neocomputers"
@@ -58,7 +52,7 @@ object NeoComputers {
Tabs.TABS.register() Tabs.TABS.register()
Sounds.SOUNDS.register() Sounds.SOUNDS.register()
ComponentRoles.mapDefaultTextures() 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) {{ EnvExecutor.runInEnv(Env.CLIENT) {{
ClientLifecycleEvent.CLIENT_SETUP.register { ClientLifecycleEvent.CLIENT_SETUP.register {
Menus.registerScreens() Menus.registerScreens()
@@ -87,7 +81,7 @@ object NeoComputers {
Networking.channels.remove() Networking.channels.remove()
} }
ClientLifecycleEvent.CLIENT_STARTED.register { ClientLifecycleEvent.CLIENT_SETUP.register {
Networking.allNodes.remove() Networking.allNodes.remove()
Networking.wirelessNodes.remove() Networking.wirelessNodes.remove()
Networking.channels.remove() Networking.channels.remove()
@@ -111,19 +105,22 @@ object NeoComputers {
packet, ctx -> packet, ctx ->
val player = ctx.player val player = ctx.player
if(player is ServerPlayer) { 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 // we have to do this because the datagen task runs in the physical server
EnvExecutor.runInEnv(Env.CLIENT) {{ 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 -> packet, ctx ->
val level = ctx.player.level() val level = ctx.player.level()
val ent = level.getBlockEntity(packet.blockPos) val ent = level.getBlockEntity(packet.blockPos)
if(ent is NodeBlockEntity) { if(ent is DeviceBlockEntity) {
ent.syncWithUpstream(packet.buffer) ent.processCommits(packet.buffers)
} }
}) })
@@ -143,7 +140,7 @@ object NeoComputers {
}} }}
EnvExecutor.runInEnv(Env.SERVER) {{ EnvExecutor.runInEnv(Env.SERVER) {{
// https://github.com/architectury/architectury-api/issues/518 // 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.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC) NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC)
}} }}

View File

@@ -27,7 +27,7 @@ import net.minecraft.world.phys.shapes.VoxelShape
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.CableEntity import org.neoflock.neocomputers.entity.CableEntity
class CableBlock() : BaseBlock(Properties.of()), EntityBlock { class CableBlock() : DeviceBlock(Properties.of()), EntityBlock {
companion object { companion object {
val NORTH = BooleanProperty.create("north") val NORTH = BooleanProperty.create("north")
val EAST = BooleanProperty.create("east") val EAST = BooleanProperty.create("east")
@@ -96,12 +96,20 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean { fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean {
val ent = level.getBlockEntity(npos) val ent = level.getBlockEntity(npos)
val blockState = level.getBlockState(pos) val blockState = level.getBlockState(pos)
val theirState = level.getBlockState(npos)
return ent is NodeBlockEntity || (ent is CableEntity && val universal = DyeColor.LIGHT_GRAY
(level.getBlockState(npos).getValue(COLOR).equals(blockState.getValue(COLOR)) || if(ent is CableEntity) {
blockState.getValue(COLOR).equals(DyeColor.LIGHT_GRAY) || val ourColor = blockState.getValue(COLOR)
level.getBlockState(npos).getValue(COLOR).equals(DyeColor.LIGHT_GRAY))) val theirColor = theirState.getValue(COLOR)
// val state: BlockState? = (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY))
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)) .add(COLOR))
} }
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? { override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state)
return CableEntity(pos, state)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? { 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)) val idx = calcIdx(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))

View File

@@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.OutgoingChatMessage import net.minecraft.network.chat.OutgoingChatMessage
import net.minecraft.network.chat.PlayerChatMessage import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
@@ -25,6 +26,16 @@ open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: Bl
val deviceNode = object : DeviceNode() { val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.STORAGE override var powerRole = PowerRole.STORAGE
override var energyCapacity: Long = capacity 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 // 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 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 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 { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
val cap: CapacitorEntity = when(tier) { val cap: CapacitorEntity = when(tier) {
1 -> CapacitorEntityTier1(blockPos, blockState) 1 -> CapacitorEntityTier1(blockPos, blockState)
@@ -54,7 +65,7 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
3 -> CapacitorEntityTier3(blockPos, blockState) 3 -> CapacitorEntityTier3(blockPos, blockState)
else -> throw UnsupportedOperationException("unsupported tier: $tier") else -> throw UnsupportedOperationException("unsupported tier: $tier")
} }
return cap.initNetworking() return cap
} }
override fun useWithoutItem( override fun useWithoutItem(
@@ -64,13 +75,12 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
player: Player, player: Player,
blockHitResult: BlockHitResult blockHitResult: BlockHitResult
): InteractionResult { ): InteractionResult {
if(!level.isClientSide()) { if(level.isClientSide()) {
val p = player as ServerPlayer
val ent = level.getBlockEntity(blockPos) val ent = level.getBlockEntity(blockPos)
if(ent is CapacitorEntity) { if(ent is CapacitorEntity) {
if(p.isCrouching) ent.deviceNode.giveEnergy(1) 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} connected)") val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} reachable)")
p.sendSystemMessage(OutgoingChatMessage.create(msg).content()) player.sendSystemMessage(OutgoingChatMessage.create(msg).content())
} }
} }
return InteractionResult.SUCCESS return InteractionResult.SUCCESS

View File

@@ -31,9 +31,10 @@ import org.neoflock.neocomputers.block.CombustionGeneratorBlock.Companion.COMBUS
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.CaseBlockEntity import org.neoflock.neocomputers.entity.CaseBlockEntity
import org.neoflock.neocomputers.entity.MachineEntity import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.sounds.Sounds 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 { companion object {
val FACING: EnumProperty<Direction> = EnumProperty.create<Direction>("facing", Direction::class.java) val FACING: EnumProperty<Direction> = EnumProperty.create<Direction>("facing", Direction::class.java)
val COMPUTER_RUNNING = BooleanProperty.create("running")!! 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)) 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<Block?, BlockState?>) { override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(COMPUTER_RUNNING) builder.add(COMPUTER_RUNNING)
@@ -115,7 +116,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
} else { } else {
// Open menu // Open menu
MenuRegistry.openMenu(player as ServerPlayer, ent) MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, ent) NodeSynchronizer.registerPlayerScreen(player, ent.deviceNode)
} }
} }
return InteractionResult.SUCCESS return InteractionResult.SUCCESS

View File

@@ -1,7 +1,11 @@
package org.neoflock.neocomputers.block package org.neoflock.neocomputers.block
import dev.architectury.networking.NetworkManager
import io.netty.buffer.Unpooled
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction 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.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock 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 net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking 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) { abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): BlockEntity(type, pos, state) {
val connetionsInDir = MutableList(Direction.entries.size) { HashSet<DeviceNode>() } val connetionsInDir = MutableList<DeviceNode?>(Direction.entries.size) { null }
var alreadySetup = false
abstract fun getDeviceNodes(): List<DeviceNode> abstract fun getDeviceNodes(): List<DeviceNode>
@@ -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. // so it is Direction.UP if we asked from the one on the top side.
abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode? abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode?
open fun processCommits(commits: Iterable<FriendlyByteBuf>) {
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 { open fun initNetworking(): DeviceBlockEntity {
getDeviceNodes().forEach { Networking.addNode(it) } if(hasLevel()) {
alreadySetup = true
Networking.addNodes(getDeviceNodes())
Direction.entries.forEach { handleConnectionsFor(it) } Direction.entries.forEach { handleConnectionsFor(it) }
}
return this return this
} }
open fun getCurrentlyConnectedNodesIn(direction: Direction): HashSet<DeviceNode> { // Cables are 1 node
open fun getCurrentlyConnectedNodeIn(direction: Direction): DeviceNode? {
val ent = level?.getBlockEntity(blockPos.relative(direction)) val ent = level?.getBlockEntity(blockPos.relative(direction))
val connected = HashSet<DeviceNode>()
if(ent is DeviceBlockEntity) { if(ent is DeviceBlockEntity) {
val node = ent.getNodeFromSide(direction.opposite) return ent.getNodeFromSide(direction.opposite)
if(node != null) connected.add(node)
} }
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) { open fun handleConnectionsFor(direction: Direction) {
// refuse connections on no node to reduce CPU load // refuse connections on no node to reduce CPU load
val node = getNodeFromSide(direction.opposite) ?: return val node = getNodeFromSide(direction.opposite) ?: return
val old = connetionsInDir[direction.ordinal] val old = connetionsInDir[direction.ordinal]
val now = getCurrentlyConnectedNodesIn(direction) val now = getCurrentlyConnectedNodeIn(direction)
// TODO: optimize this hellscape if(old?.address != now?.address) {
if(old != null) node.disconnectFrom(old)
val toKill = HashSet<DeviceNode>() if(now != null) node.connectTo(now)
old.forEach {
if(it !in now) toKill.add(it)
}
toKill.forEach { node.disconnectFrom(it) }
now.forEach {
if(it !in old) node.connectTo(it)
} }
connetionsInDir[direction.ordinal] = now connetionsInDir[direction.ordinal] = now
} }
// TODO: optimize this sometime before our test computers melt // TODO: optimize this sometime before our test computers melt
open fun tickDevice() { open fun tickDevice(level: Level) {
// Handles device connections and sync here // Handles device connections and sync here
// Process connections // we do it like this because stinky MC will call stuff before world is fully setup
Direction.entries.forEach { // and then not notify us of neighbour changes
handleConnectionsFor(it) // this is because MC is considered shit
if(!alreadySetup) {
initNetworking()
}
}
open fun sendCommitsToClient(level: Level) {
if(level is ServerLevel) {
// synchronization!
val commits = mutableListOf<FriendlyByteBuf>()
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() { override fun setRemoved() {
super.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<T> { return object : BlockEntityTicker<T> {
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T & Any) { override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T & Any) {
if(blockEntity !is DeviceBlockEntity) return if(blockEntity !is DeviceBlockEntity) return
blockEntity.tickDevice() blockEntity.tickDevice(level)
blockEntity.sendCommitsToClient(level)
} }
} }
} }

View File

@@ -23,13 +23,14 @@ import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.entity.BlockEntities import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity
import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity
import org.neoflock.neocomputers.network.NodeSynchronizer
class SolarGeneratorBlock : NodeBlock(), EntityBlock { class SolarGeneratorBlock : DeviceBlock(), EntityBlock {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState).initNetworking() override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState)
} }
// TODO: make it glow when burning // TODO: make it glow when burning
class CombustionGeneratorBlock : NodeBlock, EntityBlock { class CombustionGeneratorBlock : DeviceBlock, EntityBlock {
companion object { companion object {
val COMBUSTGEN_ACTIVE = BooleanProperty.create("active") val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
@@ -42,7 +43,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
registerDefaultState(defaultBlockState().setValue(COMBUSTGEN_ACTIVE, false)) 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( override fun useWithoutItem(
blockState: BlockState, blockState: BlockState,
@@ -54,7 +55,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
if(!level.isClientSide()) { if(!level.isClientSide()) {
val sp = player as ServerPlayer val sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get() val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get()
NodeSynchronizer.registerPlayerScreen(sp, ent) NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
MenuRegistry.openMenu(sp, ent) MenuRegistry.openMenu(sp, ent)
} }
return InteractionResult.SUCCESS return InteractionResult.SUCCESS

View File

@@ -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<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
}
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<ScreenDataPayload>(SCREEN_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
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<BeepDataPayload>(BEEP_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
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<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))
}
}
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<BlockEntity> {
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<NodeBlockEntity> {
val s = mutableSetOf<NodeBlockEntity>()
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 <T : BlockEntity> getTicker(
level: Level,
blockState: BlockState,
blockEntityType: BlockEntityType<T>
): BlockEntityTicker<T>? {
return object : BlockEntityTicker<T> {
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()
}
}
}
}

View File

@@ -15,7 +15,7 @@ import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.DeviceNode 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<Int>(Direction.entries.size) {0} val redstoneIn = Array<Int>(Direction.entries.size) {0}
val redstoneOut = Array<Int>(Direction.entries.size) {0} val redstoneOut = Array<Int>(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) override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = RedstoneIOEntity(blockPos, blockState)
fun getRedstoneIO(level: BlockGetter, blockPos: BlockPos): RedstoneIOEntity? { fun getRedstoneIO(level: BlockGetter, blockPos: BlockPos): RedstoneIOEntity? {

View File

@@ -26,23 +26,19 @@ import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.ScreenMenu import org.neoflock.neocomputers.gui.menu.ScreenMenu
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import org.neoflock.neocomputers.network.NodeSynchronizer
class ScreenBlock() : NodeBlock() { class ScreenBlock() : DeviceBlock() {
companion object { companion object {
val FACING_HORIZ: EnumProperty<Direction> = EnumProperty.create<Direction>("facing_horiz", Direction::class.java) val FACING_HORIZ: EnumProperty<Direction> = EnumProperty.create<Direction>("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 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 { init {
registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1)) registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1))
} }
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? { override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState)
val scr = ScreenEntity(blockPos, blockState)
scr.initNetworking()
return scr
}
override fun useWithoutItem( override fun useWithoutItem(
blockState: BlockState, blockState: BlockState,
@@ -52,14 +48,9 @@ class ScreenBlock() : NodeBlock() {
blockHitResult: BlockHitResult blockHitResult: BlockHitResult
): InteractionResult { ): InteractionResult {
if(!level.isClientSide) { 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 sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get() val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
NodeSynchronizer.registerPlayerScreen(sp, ent) NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider { MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider {
override fun getDisplayName(): Component = Component.literal("SCREEEEEN!") override fun getDisplayName(): Component = Component.literal("SCREEEEEN!")
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu { override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {

View File

@@ -88,12 +88,11 @@ object BlockEntities {
} }
fun registerPowerBlocks() { fun registerPowerBlocks() {
// TODO: function for the new DeviceBlockEntity PowerManager.registerPowerDevice(CAPACITOR_ENTITY.get())
//PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get()) PowerManager.registerPowerDevice(CAPACITOR2_ENTITY.get())
//PowerManager.registerPowerBlockEntity(CAPACITOR2_ENTITY.get()) PowerManager.registerPowerDevice(CAPACITOR3_ENTITY.get())
//PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get()) PowerManager.registerPowerDevice(SOLARGEN_ENTITY.get())
PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get()) PowerManager.registerPowerDevice(COMBUSTGEN_ENTITY.get())
PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get()) PowerManager.registerPowerDevice(CASE_ENTITY.get())
PowerManager.registerPowerBlockEntity(CASE_ENTITY.get())
} }
} }

View File

@@ -2,15 +2,31 @@ package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.DyeColor 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.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CableBlock import org.neoflock.neocomputers.block.CableBlock
import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR
import org.neoflock.neocomputers.block.CableBlock.Companion.getPropByDirection 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() { override fun setChanged() {
super.setChanged() super.setChanged()

View File

@@ -20,8 +20,7 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CaseBlock import org.neoflock.neocomputers.block.CaseBlock
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.item.ComponentItem import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.DeviceNode
@@ -33,8 +32,10 @@ import org.neoflock.neocomputers.utils.GenericContainer
import java.time.Duration import java.time.Duration
import kotlin.math.max import kotlin.math.max
import kotlin.math.min 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<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY) val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
var isOn = false var isOn = false
@@ -47,26 +48,28 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override val deviceNode = object : DeviceNode() { override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.CONSUMER override var powerRole = PowerRole.CONSUMER
override var energyCapacity: Long = 500 override var energyCapacity: Long = 500
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeUUID(address)
buf.writeBoolean(isOn)
buf.writeVarInt(diskActivityTime)
buf.writeVarInt(networkActivityTime)
buf.writeUtf(err ?: "")
} }
override fun encodeDownstreamData(packet: FriendlyByteBuf) { override fun processCommit(buf: FriendlyByteBuf) {
super.encodeDownstreamData(packet) super.processCommit(buf)
packet.writeBoolean(isOn) Networking.changeNodeAddress(this, buf.readUUID())
packet.writeVarInt(diskActivityTime) setRunning(buf.readBoolean())
packet.writeVarInt(networkActivityTime) diskActivityTime = buf.readVarInt()
packet.writeUtf(err ?: "") networkActivityTime = buf.readVarInt()
err = buf.readUtf().ifEmpty { null }
} }
override fun syncWithUpstream(packet: FriendlyByteBuf) { override fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {
super.syncWithUpstream(packet) super.processScreenInteraction(player, buf)
setRunning(packet.readBoolean()) val c = buf.readByte().toInt()
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) { if(c == 0x01) {
start() start()
} }
@@ -75,17 +78,33 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
} }
} }
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) { override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
super.encodeScreenData(player, packet) super.encodeScreenData(player, buf)
packet.writeBoolean(isOn) buf.writeBoolean(isOn)
packet.writeByteArray((err ?: "").encodeToByteArray()) buf.writeByteArray((err ?: "").encodeToByteArray())
packet.writeLong(deviceNode.energy) buf.writeLong(energy)
packet.writeLong(deviceNode.energyCapacity) buf.writeLong(energyCapacity)
packet.writeLong(getMachineMemoryUsed()) buf.writeLong(getMachineMemoryUsed())
packet.writeLong(getMachineMemoryTotal()) buf.writeLong(getMachineMemoryTotal())
packet.writeLong(getMachineComponentsUsed()) buf.writeLong(getMachineComponentsUsed())
packet.writeLong(getMachineComponentsTotal()) buf.writeLong(getMachineComponentsTotal())
packet.writeUtf(arch) 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} val redstoneIn = Array(Direction.entries.size) {0}
@@ -154,9 +173,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun start(): Boolean { override fun start(): Boolean {
if(isOn) return true if(isOn) return true
err = null 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 // 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") crash("@neocomputers.errors.ENOCPU")
beepAsync("-..") beepAsync("-..")
return false return false
@@ -178,9 +197,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
beepAsync("-.") beepAsync("-.")
return false return false
} }
if(arch !in archs) { if(arch !in architectures) {
// Just pick one! TODO: consult EEPROM first // Just pick one! TODO: consult EEPROM first
arch = archs.first() arch = architectures.first()
} }
beepAsync(".") beepAsync(".")
setRunning(true) setRunning(true)
@@ -194,10 +213,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
} }
override fun crash(error: String): Boolean { override fun crash(error: String): Boolean {
if(isOn) {
beepAsync("--") beepAsync("--")
sendMachineEvent(MachineCrashEvent(this, error)) sendMachineEvent(MachineCrashEvent(this, error))
}
setRunning(false) setRunning(false)
err = error err = error
return true return true
@@ -271,21 +288,4 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
setRunning(false) setRunning(false)
super.setRemoved() 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")
}
}
}
}
} }

View File

@@ -1,6 +1,7 @@
package org.neoflock.neocomputers.entity package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag 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.Level
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CombustionGeneratorBlock 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.gui.menu.CombustionGeneratorMenu
import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.utils.GenericContainer import org.neoflock.neocomputers.utils.GenericContainer
import org.neoflock.neocomputers.utils.ContainerUtils import org.neoflock.neocomputers.utils.ContainerUtils
import kotlin.math.min 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 val energyPerTick: Long = 50
var burningTimeRemaining: Int = 0 var burningTimeRemaining: Int = 0
@@ -32,6 +32,11 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
override val deviceNode = object : DeviceNode() { override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.GENERATOR override var powerRole = PowerRole.GENERATOR
override var energyCapacity: Long = 100000 override var energyCapacity: Long = 100000
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
buf.writeLong(energy)
buf.writeLong(energyCapacity)
}
} }
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(1, ItemStack.EMPTY) val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(1, ItemStack.EMPTY)
@@ -46,8 +51,8 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
return !this.isRemoved return !this.isRemoved
} }
override fun tickNode(level: Level) { override fun tickDevice(level: Level) {
super.tickNode(level) super.tickDevice(level)
// TODO: give us a block state tag for active // TODO: give us a block state tag for active
// keep combusting and shi // keep combusting and shi
@@ -79,11 +84,6 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0)) 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) { override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider) super.loadAdditional(compoundTag, provider)
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy")) deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))

View File

@@ -1,38 +1,66 @@
package org.neoflock.neocomputers.entity; package org.neoflock.neocomputers.entity;
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.locale.Language
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers 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.gui.buffer.BufferRenderer
import org.neoflock.neocomputers.network.DeviceNode import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.GPUChar import org.neoflock.neocomputers.utils.GPUChar
import org.neoflock.neocomputers.utils.TextBuffer import org.neoflock.neocomputers.utils.TextBuffer
import kotlin.text.ifEmpty
class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : 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 val deviceNode = object : DeviceNode() {
override fun received(message: Networking.Message) { override fun received(message: Networking.Message) {
super.received(message) super.received(message)
if(message is Networking.ComputerEvent) { if(message is Networking.ComputerEvent) {
// return if not directly connected
if(message.sender !in this.connections) return
val mEnv = message.machineEvent val mEnv = message.machineEvent
NeoComputers.LOGGER.info("Got message $mEnv!") NeoComputers.LOGGER.info("Got message $mEnv!")
if(mEnv is MachinePowerEvent) { if(mEnv is MachinePowerEvent) {
if(mEnv.nowRunning) { if(mEnv.nowRunning) {
textBuf.set(0, 0, address.toString()) lastError = null
} else {
textBuf.fill(0, 0, textBuf.width, textBuf.height, GPUChar(' ')) 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" 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 private var cleanrenderer: () -> Unit = { }; // TODO: THIS SUCKS, FIND A BETTER WAY
override fun encodeDownstreamData(packet: FriendlyByteBuf) { override fun tickDevice(level: Level) {
super.encodeDownstreamData(packet) super.tickDevice(level)
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)
cleanrenderer() cleanrenderer()
createscreenstuffs() createscreenstuffs()
} }
@@ -71,9 +84,29 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
private fun createscreenstuffs() { private fun createscreenstuffs() {
bound = "screen/"+deviceNode.address.toString().replace("-", "_") bound = "screen/"+deviceNode.address.toString().replace("-", "_")
if (level!!.isClientSide) { if (level!!.isClientSide) {
if(lastError == null) {
if(!isOn) {
textBuf.fill(0, 0, textBuf.width, textBuf.height)
}
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf) var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
renderer.drawBuffer() renderer.drawBuffer()
cleanrenderer = { renderer.clean() } 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() }
}
} }
} }
} }

View File

@@ -1,16 +1,16 @@
package org.neoflock.neocomputers.entity package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState 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.DeviceNode
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole 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 val energyPerTick: Long = 50
override val deviceNode = object : DeviceNode() { override val deviceNode = object : DeviceNode() {
@@ -18,8 +18,8 @@ class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : No
override var energyCapacity: Long = 50000 override var energyCapacity: Long = 50000
} }
override fun tickNode(level: Level) { override fun tickDevice(level: Level) {
super.tickNode(level) super.tickDevice(level)
val l = level val l = level
if(l.isDay) { if(l.isDay) {
deviceNode.giveEnergy(energyPerTick) deviceNode.giveEnergy(energyPerTick)

View File

@@ -27,6 +27,7 @@ class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) :
.createCompositeState(false)) .createCompositeState(false))
} }
override fun render(entity: ScreenEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) { 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 facing = entity.blockState.getValue(ScreenBlock.FACING_HORIZ)
val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1 val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1

View File

@@ -26,15 +26,11 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
Minecraft.getInstance().textureManager.register(this.id, tex) Minecraft.getInstance().textureManager.register(this.id, tex)
} }
fun dump(path: String) { fun toRGBA(color: Int): Int {
image.writeToFile(File(path)) // Minecaft lies, its AGBR
NeoComputers.LOGGER.info("DUMPED!!!") 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) { fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) {
var glyph: ArrayList<Byte> = FontProvider.map[c]!! var glyph: ArrayList<Byte> = FontProvider.map[c]!!
@@ -42,7 +38,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
for (i in 0..<CHARW) { for (i in 0..<CHARW) {
// var pixel = ((glyph[j] and ((1 shl (CHARW - i - 1)).toByte())).toInt()) ushr (CHARW - i - 1) // retardation // var pixel = ((glyph[j] and ((1 shl (CHARW - i - 1)).toByte())).toInt()) ushr (CHARW - i - 1) // retardation
var pixel = (glyph[j] and (0b10000000 ushr i).toByte()).toInt() var pixel = (glyph[j] and (0b10000000 ushr i).toByte()).toInt()
if (pixel > 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 char: GPUChar = buffer.get(i, j)
var x = i*CHARW var x = i*CHARW
var y = j*CHARH 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) if (char.c != ' ' && char.c != '\u0000') drawGlyph(x, y, char.c, char.fg)
} }
} }

View File

@@ -11,7 +11,7 @@ import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.tooltip.TooltipComponent import net.minecraft.world.inventory.tooltip.TooltipComponent
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.NeoComputers 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.menu.CaseMenu
import org.neoflock.neocomputers.gui.widget.ButtonSprites import org.neoflock.neocomputers.gui.widget.ButtonSprites
import org.neoflock.neocomputers.gui.widget.ImagerButton import org.neoflock.neocomputers.gui.widget.ImagerButton

View File

@@ -24,6 +24,8 @@ class ScreenScreen : GenericContainerScreen<ScreenMenu>{
var textBuf = TextBuffer(0, 0) var textBuf = TextBuffer(0, 0)
override fun shouldCenterTitle(): Boolean = false
override fun processScreenStatePacket(buf: FriendlyByteBuf) { override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf) super.processScreenStatePacket(buf)
textBuf.decodeContents(buf) textBuf.decodeContents(buf)

View File

@@ -2,6 +2,7 @@ package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.network.Networking.Message import org.neoflock.neocomputers.network.Networking.Message
import org.neoflock.neocomputers.network.Networking.Visibility import org.neoflock.neocomputers.network.Networking.Visibility
@@ -215,7 +216,8 @@ open class DeviceNode(_address: UUID? = null) {
outOfSync = true 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 // Meant to write the entire state as a single commit
// for clients which say they have no fucking idea what the server is storing // for clients which say they have no fucking idea what the server is storing

View File

@@ -85,12 +85,14 @@ object Networking {
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address] fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
// TODO: use setter, more convenient // TODO: use setter, more convenient
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) { fun changeNodeAddress(deviceNode: DeviceNode, address: UUID): Boolean {
if(deviceNode.address.equals(address)) return if(deviceNode.address.equals(address)) return false
if(deviceNode.address !in allNodes.get()) return if(deviceNode.address !in allNodes.get()) return false
if(address in allNodes.get()) return false
allNodes.get().remove(deviceNode.address) allNodes.get().remove(deviceNode.address)
deviceNode.address = address deviceNode.address = address
allNodes.get()[address] = deviceNode allNodes.get()[address] = deviceNode
return true
} }
fun addNode(deviceNode: DeviceNode) { fun addNode(deviceNode: DeviceNode) {
@@ -103,12 +105,17 @@ object Networking {
allNodes.get().forEach { it.value.onNodeAdded(deviceNode) } allNodes.get().forEach { it.value.onNodeAdded(deviceNode) }
} }
fun addNodes(vararg deviceNodes: DeviceNode) { fun addNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { addNode(it) } deviceNodes.forEach { addNode(it) }
} }
fun addNodes(vararg deviceNodes: DeviceNode) {
addNodes(deviceNodes.asIterable())
}
fun removeNode(deviceNode: DeviceNode) { fun removeNode(deviceNode: DeviceNode) {
if(deviceNode.address !in allNodes.get()) return if(deviceNode.address !in allNodes.get()) return
NodeSynchronizer.nodeErased(deviceNode)
allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) } allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) }
// toList() in order to copy it // toList() in order to copy it
deviceNode.connections.toList().forEach { deviceNode.connections.toList().forEach {
@@ -121,10 +128,14 @@ object Networking {
} }
} }
fun removeNodes(vararg deviceNodes: DeviceNode) { fun removeNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { removeNode(it) } deviceNodes.forEach { removeNode(it) }
} }
fun removeNodes(vararg deviceNodes: DeviceNode) {
removeNodes(deviceNodes.asIterable())
}
val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() } val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() }
fun addToChannel(channel: String, deviceNode: DeviceNode) { fun addToChannel(channel: String, deviceNode: DeviceNode) {

View File

@@ -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<FriendlyByteBuf>): CustomPacketPayload {
companion object {
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync")
val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(NODE_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> {
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<ScreenPayload>(SCREEN_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
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<ScreenDataPayload>(SCREEN_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
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<BeepDataPayload>(BEEP_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
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<ServerPlayer, DeviceNode>()
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)
}
}
}
}

View File

@@ -1,9 +1,10 @@
package org.neoflock.neocomputers.network package org.neoflock.neocomputers.network
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import org.neoflock.neocomputers.block.DeviceBlockEntity
//? if fabric { //? if fabric {
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext 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; import team.reborn.energy.api.EnergyStorage;
//?} //?}
@@ -11,27 +12,30 @@ import team.reborn.energy.api.EnergyStorage;
// the NodeBlockEntity and Node given us a way to get power from a block, we just // 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 // need to tell mods how to do it as well
object PowerManager { object PowerManager {
fun<T: NodeBlockEntity> registerPowerBlockEntity(blockEntityType: BlockEntityType<T>) { fun<T: DeviceBlockEntity> registerPowerDevice(blockEntityType: BlockEntityType<T>) {
//? if fabric { //? if fabric {
EnergyStorage.SIDED.registerForBlockEntity({ EnergyStorage.SIDED.registerForBlockEntity({
entity, dir -> object : EnergyStorage { // TODO: as this is currently written, if the node instance changes and the mod cached the conversion, we're boned. Consider fixing it.
override fun getAmount() = entity.deviceNode.energy entity, dir ->
override fun getCapacity() = entity.deviceNode.energyCapacity val node = entity.getNodeFromSide(dir ?: Direction.UP)
override fun supportsExtraction() = entity.deviceNode.powerRole != PowerRole.CONSUMER && entity.deviceNode.energyCapacity > 0 if(node == null) null else object : EnergyStorage {
override fun supportsInsertion() = entity.deviceNode.powerRole != PowerRole.GENERATOR 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 { override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.deviceNode.powerRole == PowerRole.CONSUMER) return 0 if(node.powerRole == PowerRole.CONSUMER) return 0
val taken = entity.deviceNode.withdrawEnergy(maxAmount) val taken = node.withdrawEnergy(maxAmount)
transaction?.addCloseCallback { transaction?.addCloseCallback {
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.deviceNode.giveEnergy(taken) ctx, res -> if(res.wasAborted() || !res.wasCommitted()) node.giveEnergy(taken)
} }
return taken return taken
} }
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.deviceNode.powerRole == PowerRole.GENERATOR) return 0 if(node.powerRole == PowerRole.GENERATOR) return 0
val given = entity.deviceNode.giveEnergy(maxAmount) val given = node.giveEnergy(maxAmount)
transaction?.addCloseCallback { ctx, res -> transaction?.addCloseCallback { ctx, res ->
if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.withdrawEnergy(given) if (res.wasAborted() || !res.wasCommitted()) node.withdrawEnergy(given)
} }
return given return given
} }

View File

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB