From dbd3a7b45bad7f1c1854494748606649aa583dad Mon Sep 17 00:00:00 2001 From: Mason Dechaineux Date: Tue, 23 Dec 2025 14:34:34 +1000 Subject: [PATCH] feat: allow viewing a single cable network with ALT+R-Click with the network tool --- .../sfm/client/handler/ItemWorldRenderer.java | 17 ++- .../sfm/common/item/NetworkToolItem.java | 117 ++++++++++++++---- ...erboundNetworkToolToggleOverlayPacket.java | 4 +- .../net/ServerboundNetworkToolUsePacket.java | 57 +++++++-- 4 files changed, 157 insertions(+), 38 deletions(-) diff --git a/src/main/java/ca/teamdman/sfm/client/handler/ItemWorldRenderer.java b/src/main/java/ca/teamdman/sfm/client/handler/ItemWorldRenderer.java index 21b7f66ec..0eaa122b5 100644 --- a/src/main/java/ca/teamdman/sfm/client/handler/ItemWorldRenderer.java +++ b/src/main/java/ca/teamdman/sfm/client/handler/ItemWorldRenderer.java @@ -77,6 +77,7 @@ public class ItemWorldRenderer { private static final int capabilityColor = FastColor.ARGB32.color(100, 100, 0, 255); private static final int capabilityColorLimitedView = FastColor.ARGB32.color(100, 0, 100, 255); private static final int cableColor = FastColor.ARGB32.color(100, 100, 255, 0); + private static final int noNetworkErrorColor = FastColor.ARGB32.color(200, 255, 50, 50); private static final VBOCache vboCache = new VBOCache(); @SubscribeEvent @@ -244,9 +245,19 @@ private static void handleNetworkTool( RENDER_TYPE.setupRenderState(); - drawVbo(VBOKind.NETWORK_TOOL_CABLES, poseStack, cablePositions, cableColor, event); - drawVbo(VBOKind.NETWORK_TOOL_CAPABILITIES, poseStack, capabilityPositions, capabilityColor, event); - + var selectedPos = NetworkToolItem.getSelectedNetworkBlockPos(networkTool); + if (cablePositions.isEmpty() && selectedPos != null) { + drawVbo( + VBOKind.NETWORK_TOOL_CABLES, + poseStack, + Set.of(selectedPos), + noNetworkErrorColor, + event + ); + } else { + drawVbo(VBOKind.NETWORK_TOOL_CABLES, poseStack, cablePositions, cableColor, event); + drawVbo(VBOKind.NETWORK_TOOL_CAPABILITIES, poseStack, capabilityPositions, capabilityColor, event); + } RENDER_TYPE.clearRenderState(); diff --git a/src/main/java/ca/teamdman/sfm/common/item/NetworkToolItem.java b/src/main/java/ca/teamdman/sfm/common/item/NetworkToolItem.java index ef35e7a2f..96eaf9614 100644 --- a/src/main/java/ca/teamdman/sfm/common/item/NetworkToolItem.java +++ b/src/main/java/ca/teamdman/sfm/common/item/NetworkToolItem.java @@ -1,5 +1,6 @@ package ca.teamdman.sfm.common.item; +import ca.teamdman.sfm.client.handler.NetworkToolKeyMappingHandler; import ca.teamdman.sfm.client.registry.SFMKeyMappings; import ca.teamdman.sfm.common.cablenetwork.CableNetwork; import ca.teamdman.sfm.common.cablenetwork.CableNetworkManager; @@ -27,6 +28,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; public class NetworkToolItem extends Item { public NetworkToolItem() { @@ -36,13 +38,25 @@ public NetworkToolItem() { @Override public InteractionResult onItemUseFirst( ItemStack stack, - UseOnContext pContext + UseOnContext ctx ) { - if (!pContext.getLevel().isClientSide) return InteractionResult.SUCCESS; - SFMPackets.sendToServer(new ServerboundNetworkToolUsePacket( - pContext.getClickedPos(), - pContext.getClickedFace() - )); + var level = ctx.getLevel(); + Player player = ctx.getPlayer(); + if (level.isClientSide && player != null) { + boolean pickBlock = SFMKeyMappings.isKeyDown(SFMKeyMappings.TOGGLE_NETWORK_TOOL_OVERLAY_KEY); + ServerboundNetworkToolUsePacket msg = new ServerboundNetworkToolUsePacket( + ctx.getHand(), + ctx.getClickedPos(), + ctx.getClickedFace(), + pickBlock + ); + SFMPackets.sendToServer(msg); + if (pickBlock) { + // we don't want to toggle the overlay if we're using pick-block + NetworkToolKeyMappingHandler.setExternalDebounce(); + } + return InteractionResult.SUCCESS; + } return InteractionResult.CONSUME; } @@ -80,37 +94,92 @@ public void inventoryTick( if (!isInHand) return; boolean shouldRefresh = pEntity.tickCount % 20 == 0; if (!shouldRefresh) return; - final long maxDistance = 128; - Set cablePositions = CableNetworkManager - .getNetworksInRange(pLevel, pEntity.blockPosition(), maxDistance) + regenerateCablePositions(pStack, pLevel, pPlayer); + + // remove the data stored by older versions of the mod + pStack.getOrCreateTag().remove("networks"); + } + + public static void regenerateCablePositions(ItemStack pStack, Level pLevel, Player pPlayer) { + Set cablePositions = getNetworksForOverlay(pStack, pLevel, pPlayer) .flatMap(CableNetwork::getCablePositions) .collect(Collectors.toSet()); setCablePositions(pStack, cablePositions); - Set capabilityProviderPositions = CableNetworkManager - .getNetworksInRange(pLevel, pEntity.blockPosition(), maxDistance) + Set capabilityProviderPositions = getNetworksForOverlay(pStack, pLevel, pPlayer) .flatMap(CableNetwork::getCapabilityProviderPositions) .collect(Collectors.toSet()); setCapabilityProviderPositions(pStack, capabilityProviderPositions); - - // remove the data stored by older versions of the mod - pStack.getOrCreateTag().remove("networks"); } + protected static Stream getNetworksForOverlay( + ItemStack pStack, + Level pLevel, + Player pPlayer + ) { + final long maxDistance = 128; + + BlockPos blockPos = getOverlayMode(pStack) == NetworkToolOverlayMode.SHOW_SELECTED_NETWORK + ? getSelectedNetworkBlockPos(pStack) + : null; + + if (blockPos != null) { + return CableNetworkManager.getOrRegisterNetworkFromCablePosition(pLevel, blockPos).stream(); + } else { + return CableNetworkManager.getNetworksInRange(pLevel, pPlayer.blockPosition(), maxDistance); + } + } public static boolean getOverlayEnabled(ItemStack stack) { - return !stack.getOrCreateTag().getBoolean("sfm:network_tool_overlay_disabled"); + return getOverlayMode(stack) != NetworkToolOverlayMode.HIDDEN; + } + + /** + * Returns the current enum mode for the label gun item. + */ + public static NetworkToolOverlayMode getOverlayMode(ItemStack stack) { + int ordinal = stack.getOrCreateTag().getInt("sfm:network_tool_overlay_mode"); + // fallback if out of bounds or missing + if (ordinal < 0 || ordinal >= NetworkToolOverlayMode.values().length) { + return NetworkToolOverlayMode.SHOW_ALL; + } + return NetworkToolOverlayMode.values()[ordinal]; } - public static void setOverlayEnabled( + /** + * Sets the view mode in NBT. + */ + protected static void setOverlayMode( ItemStack stack, - boolean value + NetworkToolOverlayMode mode ) { - if (value) { - stack.getOrCreateTag().remove("sfm:network_tool_overlay_disabled"); - } else { - stack.getOrCreateTag().putBoolean("sfm:network_tool_overlay_disabled", true); + stack.getOrCreateTag().putInt("sfm:network_tool_overlay_mode", mode.ordinal()); + if (mode != NetworkToolOverlayMode.SHOW_SELECTED_NETWORK) { + stack.getOrCreateTag().remove("sfm:selected_network_block_pos"); } + + // remove the data stored by older versions of the mod + stack.getOrCreateTag().remove("network_tool_overlay_disabled"); + } + + public static void cycleOverlayMode(ItemStack stack) { + NetworkToolOverlayMode current = getOverlayMode(stack); + NetworkToolOverlayMode newMode = current == NetworkToolOverlayMode.SHOW_ALL + ? NetworkToolOverlayMode.HIDDEN + : NetworkToolOverlayMode.SHOW_ALL; + setOverlayMode(stack, newMode); + } + + public static void setSelectedNetworkBlockPos(ItemStack stack, BlockPos pos) { + setOverlayMode(stack, NetworkToolOverlayMode.SHOW_SELECTED_NETWORK); + stack.getOrCreateTag().put("sfm:selected_network_block_pos", NbtUtils.writeBlockPos(pos)); + } + + @Nullable + public static BlockPos getSelectedNetworkBlockPos(ItemStack stack) { + return stack.getOrCreateTag().contains("sfm:selected_network_block_pos") + ? NbtUtils.readBlockPos(stack.getOrCreateTag().getCompound("sfm:selected_network_block_pos")) + : null; } public static void setCablePositions( @@ -156,4 +225,10 @@ public static Set getCapabilityProviderPositions(ItemStack stack) { .map(NbtUtils::readBlockPos) .collect(Collectors.toSet()); } + + public enum NetworkToolOverlayMode { + SHOW_ALL, + SHOW_SELECTED_NETWORK, + HIDDEN + } } diff --git a/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolToggleOverlayPacket.java b/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolToggleOverlayPacket.java index 94aec0908..77c3d8866 100644 --- a/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolToggleOverlayPacket.java +++ b/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolToggleOverlayPacket.java @@ -37,8 +37,8 @@ public void handle( if (sender == null) return; ItemStack networkToolItemStack = sender.getItemInHand(msg.hand); if (networkToolItemStack.getItem() == SFMItems.NETWORK_TOOL_ITEM.get()) { - boolean active = NetworkToolItem.getOverlayEnabled(networkToolItemStack); - NetworkToolItem.setOverlayEnabled(networkToolItemStack, !active); + NetworkToolItem.cycleOverlayMode(networkToolItemStack); + NetworkToolItem.regenerateCablePositions(networkToolItemStack, sender.getLevel(), sender); } } diff --git a/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolUsePacket.java b/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolUsePacket.java index dcd36532d..d0880abc4 100644 --- a/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolUsePacket.java +++ b/src/main/java/ca/teamdman/sfm/common/net/ServerboundNetworkToolUsePacket.java @@ -5,6 +5,7 @@ import ca.teamdman.sfm.common.capability.SFMBlockCapabilityDiscovery; import ca.teamdman.sfm.common.capability.SFMBlockCapabilityKind; import ca.teamdman.sfm.common.capability.SFMWellKnownCapabilities; +import ca.teamdman.sfm.common.item.NetworkToolItem; import ca.teamdman.sfm.common.registry.SFMPackets; import ca.teamdman.sfm.common.registry.SFMResourceTypes; import ca.teamdman.sfm.common.util.SFMDirections; @@ -15,6 +16,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -26,9 +28,10 @@ import java.util.stream.Collectors; public record ServerboundNetworkToolUsePacket( + InteractionHand hand, BlockPos blockPosition, - - Direction blockFace + Direction blockFace, + boolean isOverlayToggleModifierActive ) implements SFMPacket { public static class Daddy implements SFMPacketDaddy { @Override @@ -43,16 +46,20 @@ public void encode( FriendlyByteBuf friendlyByteBuf ) { + friendlyByteBuf.writeEnum(msg.hand); friendlyByteBuf.writeBlockPos(msg.blockPosition); friendlyByteBuf.writeEnum(msg.blockFace); + friendlyByteBuf.writeBoolean(msg.isOverlayToggleModifierActive); } @Override public ServerboundNetworkToolUsePacket decode(FriendlyByteBuf friendlyByteBuf) { return new ServerboundNetworkToolUsePacket( + friendlyByteBuf.readEnum(InteractionHand.class), friendlyByteBuf.readBlockPos(), - friendlyByteBuf.readEnum(Direction.class) + friendlyByteBuf.readEnum(Direction.class), + friendlyByteBuf.readBoolean() ); } @@ -62,13 +69,39 @@ public void handle( SFMPacketHandlingContext context ) { + ServerPlayer player = context.sender(); + if (player == null) return; + Level level = player.getLevel(); + BlockPos pos = msg.blockPosition(); + if (!level.isLoaded(pos)) return; + if (msg.isOverlayToggleModifierActive) { + handleOverlayFocusSelect(player, level, pos, msg.hand); + } else { + handleBlockInspectionRequest(player, level, pos, msg.blockFace()); + } + } + + private void handleOverlayFocusSelect( + ServerPlayer player, + Level level, + BlockPos pos, + InteractionHand hand + ) { + var networkToolStack = player.getItemInHand(hand); + if (!(networkToolStack.getItem() instanceof NetworkToolItem)) { + return; + } + NetworkToolItem.setSelectedNetworkBlockPos(networkToolStack, pos); + NetworkToolItem.regenerateCablePositions(networkToolStack, level, player); + } + + public void handleBlockInspectionRequest( + ServerPlayer player, + Level level, + BlockPos pos, + Direction blockFace + ) { { - // we don't know if the player has the program edit screen open from a manager or a disk in hand - ServerPlayer player = context.sender(); - if (player == null) return; - Level level = player.getLevel(); - BlockPos pos = msg.blockPosition(); - if (!level.isLoaded(pos)) return; StringBuilder payload = new StringBuilder() .append("---- block position ----\n") .append(pos) @@ -118,16 +151,16 @@ public void handle( } Direction[] directions = new Direction[SFMDirections.DIRECTIONS_WITHOUT_NULL.length + 1]; - directions[0] = msg.blockFace; + directions[0] = blockFace; directions[1] = null; int assignmentIndex = 2; for (Direction direction : SFMDirections.DIRECTIONS_WITHOUT_NULL) { - if (direction == msg.blockFace) continue; + if (direction == blockFace) continue; directions[assignmentIndex++] = direction; } String[] messages = new String[directions.length]; - messages[0] = String.format("---- exports for selected face: %s ----", msg.blockFace); + messages[0] = String.format("---- exports for selected face: %s ----", blockFace); for (int i = 1; i < directions.length; i++) { messages[i] = String.format("---- exports for face: %s ----", directions[i]); }