1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-27 16:02:17 +00:00

Implement MonitorWatcher functionality.

Note that I did not hook into ChunkMap in the same place as Forge's
ChunkWatchEvent. In my testing the Forge hook location doesn't *only*
fire when a player begins watching the chunk, instead it fires every
time the chunk is checked against the player's view distance. This
results in the server spamming packets for static terminals as players
move around. Perhaps I'm reading the Forge patch wrong...
This commit is contained in:
Toad-Dev 2021-12-07 00:18:47 -08:00 committed by Toad-Dev
parent 745b732e87
commit 6c73eb7df1
8 changed files with 129 additions and 58 deletions

View File

@ -14,7 +14,7 @@ import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.fabric.events.ClientUnloadWorldEvent;
import dan200.computercraft.fabric.events.ComputerCraftCustomEvents;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.IColouredItem;
@ -23,6 +23,7 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
@ -60,7 +61,7 @@ public final class ComputerCraftProxyClient implements ClientModInitializer
}
} );
ClientUnloadWorldEvent.EVENT.register( () -> ClientMonitor.destroyAll() );
ComputerCraftCustomEvents.CLIENT_UNLOAD_WORLD_EVENT.register( () -> ClientMonitor.destroyAll() );
// Config
ClientLifecycleEvents.CLIENT_STARTED.register( Config::clientStarted );
@ -70,6 +71,7 @@ public final class ComputerCraftProxyClient implements ClientModInitializer
public void onInitializeClient()
{
FrameInfo.init();
MonitorWatcher.init();
registerContainers();
// While turtles themselves are not transparent, their upgrades may be.

View File

@ -1,23 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.fabric.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@FunctionalInterface
public interface ClientUnloadWorldEvent
{
Event<ClientUnloadWorldEvent> EVENT = EventFactory.createArrayBacked( ClientUnloadWorldEvent.class,
callbacks -> () -> {
for( ClientUnloadWorldEvent callback : callbacks )
{
callback.onClientUnloadWorld();
}
} );
void onClientUnloadWorld();
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.fabric.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
public final class ComputerCraftCustomEvents
{
public static final Event<ClientUnloadWorld> CLIENT_UNLOAD_WORLD_EVENT = EventFactory.createArrayBacked( ClientUnloadWorld.class,
callbacks -> () -> {
for( ClientUnloadWorld callback : callbacks )
{
callback.onClientUnloadWorld();
}
} );
public static final Event<ServerPlayerLoadedChunk> SERVER_PLAYER_LOADED_CHUNK_EVENT = EventFactory.createArrayBacked( ServerPlayerLoadedChunk.class,
callbacks -> ( serverPlayer, chunkPos ) -> {
for( ServerPlayerLoadedChunk callback : callbacks )
{
callback.onServerPlayerLoadedChunk( serverPlayer, chunkPos );
}
} );
@FunctionalInterface
public interface ClientUnloadWorld
{
void onClientUnloadWorld();
}
@FunctionalInterface
public interface ServerPlayerLoadedChunk
{
void onServerPlayerLoadedChunk( ServerPlayer player, ChunkPos chunkPos );
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.fabric.mixin;
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.commons.lang3.mutable.MutableObject;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin( ChunkMap.class )
public class MixinChunkMap
{
@Final
@Shadow
ServerLevel level;
/*
* This mixin mimics the logic of Forge's ChunkWatchEvent.Watch but I don't believe it behaves as expected. Instead
* of firing once when the player initially come in server view distance of a chunk, it fires every time
* the chunk is checked against the player's view distance. This continually happens every tick that the player
* moves (or even rotates in place).
*/
// @Inject( method = "updateChunkTracking", at = @At( value = "HEAD" ) )
// public void updateChunkTracking( ServerPlayer serverPlayer, ChunkPos chunkPos, MutableObject<ClientboundLevelChunkWithLightPacket> mutableObject, boolean bl, boolean bl2, CallbackInfo ci )
// {
// if( serverPlayer.level == this.level && bl )
// {
// ComputerCraftCustomEvents.SERVER_PLAYER_LOADED_CHUNK_EVENT.invoker().onServerPlayerLoadedChunk( serverPlayer, chunkPos );
// }
// }
// This version behaves as expected in my testing.
@Inject( method = "playerLoadedChunk", at = @At( value = "HEAD" ) )
private void playerLoadedChunk( ServerPlayer serverPlayer, MutableObject<ClientboundLevelChunkWithLightPacket> mutableObject, LevelChunk levelChunk, CallbackInfo ci )
{
MonitorWatcher.onWatch( serverPlayer, levelChunk.getPos() );
}
}

View File

@ -6,7 +6,7 @@
package dan200.computercraft.fabric.mixin;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.fabric.events.ClientUnloadWorldEvent;
import dan200.computercraft.fabric.events.ComputerCraftCustomEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientLevel;
@ -27,12 +27,12 @@ public abstract class MixinMinecraft
@Inject( method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At( "RETURN" ) )
private void disconnectAfter( Screen screen, CallbackInfo info )
{
ClientUnloadWorldEvent.EVENT.invoker().onClientUnloadWorld();
ComputerCraftCustomEvents.CLIENT_UNLOAD_WORLD_EVENT.invoker().onClientUnloadWorld();
}
@Inject( method = "setLevel", at = @At( "RETURN" ) )
private void setLevel( ClientLevel world, CallbackInfo info )
{
ClientUnloadWorldEvent.EVENT.invoker().onClientUnloadWorld();
ComputerCraftCustomEvents.CLIENT_UNLOAD_WORLD_EVENT.invoker().onClientUnloadWorld();
}
}

View File

@ -25,6 +25,7 @@ import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
@ -32,6 +33,7 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec3;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -122,13 +124,8 @@ public final class NetworkHandler
public static void sendToAllTracking( NetworkMessage packet, LevelChunk chunk )
{
for( Player player : chunk.getLevel().players() )
{
if( chunk.getLevel().dimension().location() == player.getCommandSenderWorld().dimension().location() && player.chunkPosition().equals( chunk.getPos() ) )
{
((ServerPlayer) player).connection.send( new ClientboundCustomPayloadPacket( ID, encode( packet ) ) );
}
}
Consumer<ServerPlayer> sender = p -> p.connection.send( new ClientboundCustomPayloadPacket( ID, encode( packet ) ) );
((ServerChunkCache)chunk.getLevel().getChunkSource()).chunkMap.getPlayers( chunk.getPos(), false ).forEach( sender );
}
/**

View File

@ -6,6 +6,7 @@
package dan200.computercraft.shared.peripheral.monitor;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.fabric.events.ComputerCraftCustomEvents;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.client.MonitorClientMessage;
import dan200.computercraft.shared.network.client.TerminalState;
@ -14,7 +15,10 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import java.util.ArrayDeque;
@ -31,11 +35,8 @@ public final class MonitorWatcher
public static void init()
{
ServerTickEvents.END_SERVER_TICK.register( server -> {
onTick( server );
} );
ServerTickEvents.END_SERVER_TICK.register( MonitorWatcher::onTick );
ComputerCraftCustomEvents.SERVER_PLAYER_LOADED_CHUNK_EVENT.register( MonitorWatcher::onWatch );
}
static void enqueue( TileMonitor monitor )
@ -47,24 +48,23 @@ public final class MonitorWatcher
watching.add( monitor );
}
// public static void onWatch( ChunkWatchEvent.Watch event )
// {
// ChunkPos chunkPos = event.getPos();
// LevelChunk chunk = (LevelChunk) event.getWorld().getChunk( chunkPos.x, chunkPos.z, ChunkStatus.FULL, false );
// if( chunk == null ) return;
//
// for( BlockEntity te : chunk.getBlockEntities().values() )
// {
// // Find all origin monitors who are not already on the queue.
// if( !(te instanceof TileMonitor monitor) ) continue;
//
// ServerMonitor serverMonitor = getMonitor( monitor );
// if( serverMonitor == null || monitor.enqueued ) continue;
//
// // 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 ) );
// }
// }
public static void onWatch( ServerPlayer serverPlayer, ChunkPos chunkPos )
{
LevelChunk chunk = (LevelChunk) serverPlayer.getLevel().getChunk( chunkPos.x, chunkPos.z, ChunkStatus.FULL, false );
if( chunk == null ) return;
for( BlockEntity te : chunk.getBlockEntities().values() )
{
// Find all origin monitors who are not already on the queue.
if( !(te instanceof TileMonitor monitor) ) continue;
ServerMonitor serverMonitor = getMonitor( monitor );
if( serverMonitor == null || monitor.enqueued ) continue;
// 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( serverPlayer, monitor ) );
}
}
public static void onTick( MinecraftServer server )
{

View File

@ -7,6 +7,7 @@
"MinecraftServerAccess",
"MixinBlock",
"MixinChunkMap",
"MixinEntity",
"MixinLevel",
"MixinMatrix4f",