reduced network bill by 99.99999% trust

This commit is contained in:
2026-04-28 17:57:48 +03:00
parent 9ac53a0f0e
commit 02114fc02a
9 changed files with 119 additions and 15 deletions

View File

@@ -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 // we have to do this because the datagen task runs in the physical server

View File

@@ -2,10 +2,15 @@ package org.neoflock.neocomputers.block
import dev.architectury.networking.NetworkManager import dev.architectury.networking.NetworkManager
import io.netty.buffer.Unpooled 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.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
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
@@ -27,6 +32,7 @@ abstract class SingleDeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos,
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<DeviceNode?>(Direction.entries.size) { null } val connetionsInDir = MutableList<DeviceNode?>(Direction.entries.size) { null }
var alreadySetup = false var alreadySetup = false
var receivedServerState = false
abstract fun getDeviceNodes(): List<DeviceNode> abstract fun getDeviceNodes(): List<DeviceNode>
@@ -36,6 +42,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state:
abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode? abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode?
open fun processCommits(commits: Iterable<FriendlyByteBuf>) { open fun processCommits(commits: Iterable<FriendlyByteBuf>) {
receivedServerState = true
val devs = getDeviceNodes() val devs = getDeviceNodes()
for (buf in commits) { for (buf in commits) {
val idx = buf.readVarInt() val idx = buf.readVarInt()
@@ -89,32 +96,71 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state:
} }
open fun sendCommitsToClient(level: Level) { open fun sendCommitsToClient(level: Level) {
if(level is ServerLevel) { if(level !is ServerLevel) return
// synchronization! // synchronization!
val commits = mutableListOf<FriendlyByteBuf>() val commits = mutableListOf<FriendlyByteBuf>()
val devs = getDeviceNodes() val devs = getDeviceNodes()
for((i, dev) in devs.withIndex()) { 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(dev.outOfSync) {
dev.outOfSync = false dev.outOfSync = false
val buf = FriendlyByteBuf(Unpooled.buffer()) val buf = FriendlyByteBuf(Unpooled.buffer())
buf.writeVarInt(i) buf.writeVarInt(i)
dev.writeFullStateCommit(buf) dev.writeFullStateCommit(buf)
commits.addLast(buf) commits.addLast(buf)
} }
if(commits.isNotEmpty()) { }
level.players().forEach { if(commits.isNotEmpty()) {
val dist = it.position().distanceTo(blockPos.center) level.players().forEach {
if(dist < 100) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits)) 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<FriendlyByteBuf>()
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() { override fun setRemoved() {
super.setRemoved() super.setRemoved()
alreadySetup = false alreadySetup = false
Networking.removeNodes(getDeviceNodes()) 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 { 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 if(blockEntity !is DeviceBlockEntity) return
blockEntity.tickDevice(level) blockEntity.tickDevice(level)
blockEntity.sendCommitsToClient(level) blockEntity.sendCommitsToClient(level)
if(level.isClientSide) {
blockEntity.requestServerState()
}
} }
} }
} }

View File

@@ -21,6 +21,11 @@ class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(Bl
return return
} }
override fun requestServerState() {
// no state, we don't bother
return
}
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? { override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? {
if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) { if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) {
return deviceNode return deviceNode

View File

@@ -152,6 +152,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceB
fun setRunning(value: Boolean) { fun setRunning(value: Boolean) {
if(isOn == value) return if(isOn == value) return
deviceNode.markChanged()
NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value") NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value")
isOn = value isOn = value
val world = level ?: return val world = level ?: return

View File

@@ -35,9 +35,11 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
textBuf.set(0, 0, address.toString()) textBuf.set(0, 0, address.toString())
} }
isOn = mEnv.nowRunning isOn = mEnv.nowRunning
markChanged()
} }
if(mEnv is MachineCrashEvent) { if(mEnv is MachineCrashEvent) {
lastError = mEnv.error lastError = mEnv.error
markChanged()
} }
} }
} }

View File

@@ -70,7 +70,7 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
if(hasShiftDown()) { 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.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.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 return msgs
} }

View File

@@ -52,3 +52,10 @@ interface ComponentItem {
fun onMachineEvent(itemStack: ItemStack, machine: MachineEntity, event: MachineEvent) {} 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
}

View File

@@ -211,7 +211,7 @@ open class DeviceNode(_address: UUID? = null) {
// Network synchronization with the NodeSynchronizer // Network synchronization with the NodeSynchronizer
// TODO: process shi // TODO: process shi
var outOfSync = true var outOfSync = false
fun markChanged() { fun markChanged() {
outOfSync = true outOfSync = true
} }
@@ -227,6 +227,12 @@ open class DeviceNode(_address: UUID? = null) {
open fun processCommit(buf: FriendlyByteBuf) {} 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 class WirelessEndpoint(address: UUID?) : DeviceNode(address) {
abstract fun getRange(): Double abstract fun getRange(): Double

View File

@@ -15,10 +15,12 @@ import org.neoflock.neocomputers.NeoComputers
import java.time.Duration import java.time.Duration
object NodeSynchronizer { object NodeSynchronizer {
val MAX_STATE_DISTANCE_ALLOWED = 128
class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List<FriendlyByteBuf>): CustomPacketPayload { class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List<FriendlyByteBuf>): CustomPacketPayload {
companion object { companion object {
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync") val BLOCKDEV_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_sync")
val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(NODE_SYNC_ID) val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(BLOCKDEV_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> { val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> {
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload { override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload {
val blockPos = buf.readBlockPos() val blockPos = buf.readBlockPos()
@@ -45,6 +47,25 @@ object NodeSynchronizer {
override fun type() = TYPE 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<DeviceBlockStateRequest>(BLOCKDEV_REQ_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStateRequest> {
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 { class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object { companion object {
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync") val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")