diff --git a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt index 497bb30..b5ed1e2 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt @@ -111,6 +111,19 @@ object NeoComputers { } } }) + + NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.DeviceBlockStateRequest.TYPE, NodeSynchronizer.DeviceBlockStateRequest.CODEC, { + packet, ctx -> + val player = ctx.player + val level = player.level() + val dist = packet.blockPos.center.distanceTo(player.position()) + if(player is ServerPlayer && dist <= NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) { + val ent = level.getBlockEntity(packet.blockPos) + if(ent is DeviceBlockEntity) { + ent.sendStateToPlayer(player) + } + } + }) } // we have to do this because the datagen task runs in the physical server diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt index cb035a2..86c54b4 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt @@ -2,10 +2,15 @@ package org.neoflock.neocomputers.block import dev.architectury.networking.NetworkManager import io.netty.buffer.Unpooled +import net.minecraft.client.Minecraft +import net.minecraft.client.multiplayer.ClientLevel 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.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 @@ -27,6 +32,7 @@ abstract class SingleDeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): BlockEntity(type, pos, state) { val connetionsInDir = MutableList(Direction.entries.size) { null } var alreadySetup = false + var receivedServerState = false abstract fun getDeviceNodes(): List @@ -36,6 +42,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode? open fun processCommits(commits: Iterable) { + receivedServerState = true val devs = getDeviceNodes() for (buf in commits) { val idx = buf.readVarInt() @@ -89,32 +96,71 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: } 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) + if(level !is ServerLevel) return + // synchronization! + val commits = mutableListOf() + val devs = getDeviceNodes() + for((i, dev) in devs.withIndex()) { + if(dev.outOfSync) { 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)) - } + } + if(commits.isNotEmpty()) { + level.players().forEach { + val dist = it.position().distanceTo(blockPos.center) + if(dist < 100) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits)) } } } + open fun sendStateToPlayer(player: ServerPlayer) { + val world = level!! + if(world !is ServerLevel) return + // synchronization! + val commits = mutableListOf() + val devs = getDeviceNodes() + for((i, dev) in devs.withIndex()) { + val buf = FriendlyByteBuf(Unpooled.buffer()) + buf.writeVarInt(i) + dev.writeFullStateCommit(buf) + commits.addLast(buf) + } + if(commits.isNotEmpty()) { + world.players().forEach { + val dist = it.position().distanceTo(blockPos.center) + if(dist <= NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits)) + } + } + } + + open fun requestServerState() { + // no point + if(receivedServerState) return + // we're the server bro :sob: + if(level?.isClientSide != true) return + val player = Minecraft.getInstance().player ?: return + // we assume the player will just reject, so we save on bandwidth + if(player.position().distanceTo(blockPos.center) > NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) return + NetworkManager.sendToServer(NodeSynchronizer.DeviceBlockStateRequest(blockPos)) + } + override fun setRemoved() { super.setRemoved() alreadySetup = false Networking.removeNodes(getDeviceNodes()) } + + override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) { + super.loadAdditional(tag, registries) + for (node in getDeviceNodes()) { + node.markChanged() + } + receivedServerState = false + } } abstract class DeviceBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock { @@ -128,6 +174,9 @@ abstract class DeviceBlock(properties: Properties = Properties.of()): BaseBlock( if(blockEntity !is DeviceBlockEntity) return blockEntity.tickDevice(level) blockEntity.sendCommitsToClient(level) + if(level.isClientSide) { + blockEntity.requestServerState() + } } } } diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt index ff804e3..53e5c84 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CableEntity.kt @@ -21,6 +21,11 @@ class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(Bl return } + override fun requestServerState() { + // no state, we don't bother + return + } + override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? { if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) { return deviceNode diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt index 60d2a80..e6a7a9d 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/CaseBlockEntity.kt @@ -152,6 +152,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceB fun setRunning(value: Boolean) { if(isOn == value) return + deviceNode.markChanged() NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value") isOn = value val world = level ?: return diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt index 72e2536..b649578 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt @@ -35,9 +35,11 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) : textBuf.set(0, 0, address.toString()) } isOn = mEnv.nowRunning + markChanged() } if(mEnv is MachineCrashEvent) { lastError = mEnv.error + markChanged() } } } 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 02c8ba3..df6c6a7 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/gui/screen/CaseScreen.kt @@ -70,7 +70,7 @@ class CaseScreen : GenericContainerScreen { if(hasShiftDown()) { msgs.addLast(Component.translatable("neocomputers.computer.energy", energy, maxEnergy).withStyle(if(energy < maxEnergy/5) ChatFormatting.RED else ChatFormatting.WHITE)) msgs.addLast(Component.translatable("neocomputers.computer.memory", Formatting.formatMemory(memory), Formatting.formatMemory(maxMemory))) - msgs.addLast(Component.translatable("neocomputers.computer.components", components, maxEnergy).withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED)) + msgs.addLast(Component.translatable("neocomputers.computer.components", components, maxComponents).withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED)) } return msgs } diff --git a/src/main/kotlin/org/neoflock/neocomputers/item/ComponentItem.kt b/src/main/kotlin/org/neoflock/neocomputers/item/ComponentItem.kt index 86a4c60..05ef564 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/item/ComponentItem.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/item/ComponentItem.kt @@ -51,4 +51,11 @@ interface ComponentItem { } fun onMachineEvent(itemStack: ItemStack, machine: MachineEntity, event: MachineEvent) {} +} + +// A special ComponentItem which specifies upgrades specific to the relay +interface RelayUpgrade: ComponentItem { + fun getRelayInterval(itemStack: ItemStack): Int? = null + fun getRelayBufferSize(itemStack: ItemStack): Int? = null + fun getRelayQueueSize(itemStack: ItemStack): Int? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt b/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt index 5a1c1a5..3dd2660 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt @@ -211,7 +211,7 @@ open class DeviceNode(_address: UUID? = null) { // Network synchronization with the NodeSynchronizer // TODO: process shi - var outOfSync = true + var outOfSync = false fun markChanged() { outOfSync = true } @@ -227,6 +227,12 @@ open class DeviceNode(_address: UUID? = null) { open fun processCommit(buf: FriendlyByteBuf) {} } +// Used by the relay +// If the ComponentItem in the card slot +interface ConventionalNetworkDevice { + fun sendClassicPacket(packet: Networking.ClassicPacket) +} + abstract class WirelessEndpoint(address: UUID?) : DeviceNode(address) { abstract fun getRange(): Double diff --git a/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt b/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt index b6daccd..d333692 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/network/NodeSynchronizer.kt @@ -15,10 +15,12 @@ import org.neoflock.neocomputers.NeoComputers import java.time.Duration object NodeSynchronizer { + val MAX_STATE_DISTANCE_ALLOWED = 128 + 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 BLOCKDEV_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_sync") + val TYPE = CustomPacketPayload.Type(BLOCKDEV_SYNC_ID) val CODEC = object : StreamCodec { override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload { val blockPos = buf.readBlockPos() @@ -45,6 +47,25 @@ object NodeSynchronizer { override fun type() = TYPE } + class DeviceBlockStateRequest(var blockPos: BlockPos): CustomPacketPayload { + companion object { + val BLOCKDEV_REQ_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_statereq") + val TYPE = CustomPacketPayload.Type(BLOCKDEV_REQ_ID) + val CODEC = object : StreamCodec { + override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStateRequest { + val blockPos = buf.readBlockPos() + return DeviceBlockStateRequest(blockPos) + } + + override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStateRequest) { + buf.writeBlockPos(payload.blockPos) + } + } + } + + override fun type() = TYPE + } + class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload { companion object { val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")