From 154474928255049fdf2143df1b37666886c4e7bb Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 18 Jan 2021 22:20:48 +0000 Subject: [PATCH] Defer sending monitor updates until tick end We send monitor updates when a player starts watching a chunk. However, the block/tile data has not been sent when this event is fired, and so the packet is entirely ignored. Instead, we now queue a "send this" task, which is then dispatched on the next tick end. I have memories of this working on 1.12, so either something changed in an update or I'm a complete idiot. Both are possible. Fixes #687 --- .../peripheral/monitor/MonitorWatcher.java | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java index 13f4a71f2..888e316c7 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java @@ -9,6 +9,7 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.client.MonitorClientMessage; import dan200.computercraft.shared.network.client.TerminalState; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; @@ -28,6 +29,7 @@ import java.util.Queue; public final class MonitorWatcher { private static final Queue watching = new ArrayDeque<>(); + private static final Queue playerUpdates = new ArrayDeque<>(); private MonitorWatcher() { @@ -58,10 +60,8 @@ public final class MonitorWatcher ServerMonitor serverMonitor = getMonitor( monitor ); if( serverMonitor == null || monitor.enqueued ) continue; - // We use the cached terminal state if available - this is guaranteed to - TerminalState state = monitor.cached; - if( state == null ) state = monitor.cached = serverMonitor.write(); - NetworkHandler.sendToPlayer( event.getPlayer(), new MonitorClientMessage( monitor.getBlockPos(), state ) ); + // The chunk hasn't been sent to the client yet, so we can't send an update. Do it on tick end. + playerUpdates.add( new PlayerUpdate( event.getPlayer(), monitor ) ); } } @@ -70,6 +70,23 @@ public final class MonitorWatcher { if( event.phase != TickEvent.Phase.END ) return; + PlayerUpdate playerUpdate; + while( (playerUpdate = playerUpdates.poll()) != null ) + { + TileMonitor tile = playerUpdate.monitor; + if( tile.enqueued || tile.isRemoved() ) continue; + + ServerMonitor monitor = getMonitor( tile ); + if( monitor == null ) continue; + + // Some basic sanity checks to the player. It's possible they're no longer within range, but that's harder + // to track efficiently. + ServerPlayerEntity player = playerUpdate.player; + if( !player.isAlive() || player.getLevel() != tile.getLevel() ) continue; + + NetworkHandler.sendToPlayer( playerUpdate.player, new MonitorClientMessage( tile.getBlockPos(), getState( tile, monitor ) ) ); + } + long limit = ComputerCraft.monitorBandwidth; boolean obeyLimit = limit > 0; @@ -90,7 +107,7 @@ public final class MonitorWatcher continue; } - TerminalState state = tile.cached = monitor.write(); + TerminalState state = getState( tile, monitor ); NetworkHandler.sendToAllTracking( new MonitorClientMessage( pos, state ), chunk ); limit -= state.size(); @@ -101,4 +118,23 @@ public final class MonitorWatcher { return !monitor.isRemoved() && monitor.getXIndex() == 0 && monitor.getYIndex() == 0 ? monitor.getCachedServerMonitor() : null; } + + private static TerminalState getState( TileMonitor tile, ServerMonitor monitor ) + { + TerminalState state = tile.cached; + if( state == null ) state = tile.cached = monitor.write(); + return state; + } + + private static final class PlayerUpdate + { + final ServerPlayerEntity player; + final TileMonitor monitor; + + private PlayerUpdate( ServerPlayerEntity player, TileMonitor monitor ) + { + this.player = player; + this.monitor = monitor; + } + } }