From d6207f0623ab8f03c1f7a7baa4cd37f810cc7f99 Mon Sep 17 00:00:00 2001 From: Integer Limit <103940576+IntegerLimit@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:57:21 +1100 Subject: [PATCH] Better P2P: Sorting Improvements + Show Distance --- .../betterp2p/AccessibleInfoList.java | 8 ++ .../betterp2p/AccessibleInfoWrapper.java | 10 ++ .../integration/betterp2p/SortModes.java | 67 ++++++++++ .../betterp2p/GuiAdvancedMemoryCardMixin.java | 13 +- .../mixin/betterp2p/InfoListMixin.java | 115 ++++++++++++++++++ .../mixin/betterp2p/InfoWrapperMixin.java | 79 ++++++++++++ .../mixin/betterp2p/WidgetP2PDeviceMixin.java | 54 +++++--- .../mixin/jei/GuiIngredientMixin.java | 2 +- .../resources/assets/nomilabs/lang/en_us.lang | 2 + .../resources/mixins.nomilabs.betterp2p.json | 2 + 10 files changed, 334 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoList.java create mode 100644 src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoWrapper.java create mode 100644 src/main/java/com/nomiceu/nomilabs/integration/betterp2p/SortModes.java create mode 100644 src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoListMixin.java create mode 100644 src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoWrapperMixin.java diff --git a/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoList.java b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoList.java new file mode 100644 index 00000000..799ed40c --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoList.java @@ -0,0 +1,8 @@ +package com.nomiceu.nomilabs.integration.betterp2p; + +import net.minecraft.util.math.Vec3d; + +public interface AccessibleInfoList { + + void labs$setPlayerPos(Vec3d pos); +} diff --git a/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoWrapper.java b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoWrapper.java new file mode 100644 index 00000000..cb23868e --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/AccessibleInfoWrapper.java @@ -0,0 +1,10 @@ +package com.nomiceu.nomilabs.integration.betterp2p; + +import net.minecraft.util.math.Vec3d; + +public interface AccessibleInfoWrapper { + + void labs$calculateDistance(Vec3d playerPos); + + double labs$getDistance(); +} diff --git a/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/SortModes.java b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/SortModes.java new file mode 100644 index 00000000..2032a128 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/integration/betterp2p/SortModes.java @@ -0,0 +1,67 @@ +package com.nomiceu.nomilabs.integration.betterp2p; + +import java.util.Comparator; +import java.util.function.Function; + +import com.projecturanus.betterp2p.client.gui.InfoWrapper; + +public enum SortModes { + + DEFAULT(SortModes::compDefault); + + private final Function> compFromSelected; + + /** + * Create a Sort Mode. + * + * @param compFromSelected Function that takes the selected p2p and returns a comparator. + * You do not need to handle the case where either one is the selected p2p. + * Note that item smaller = in front! + * Selected p2p may be null! + */ + SortModes(Function> compFromSelected) { + this.compFromSelected = compFromSelected; + } + + public Comparator getComp(InfoWrapper selected) { + var applyComp = compFromSelected.apply(selected); + return (a, b) -> { + if (selected != null) { + // Selected first + if (a.getLoc().equals(selected.getLoc())) return -1; + if (b.getLoc().equals(selected.getLoc())) return 1; + } + + return applyComp.compare(a, b); + }; + } + + /* Sorters */ + private static Comparator compDefault(InfoWrapper selected) { + return (a, b) -> { + if (selected != null) { + // Same freq. as selected, priority over rest + // Checking for unbound is not needed, just have all unbound at front if selected is unbound + if (a.getFrequency() == selected.getFrequency()) { + if (b.getFrequency() != selected.getFrequency()) return -1; + return compareTypeThenDist(a, b); + } + + if (b.getFrequency() == selected.getFrequency()) return 1; + } + if (a.getFrequency() != b.getFrequency()) return b.getFrequency() - a.getFrequency(); + + return compareTypeThenDist(a, b); + }; + } + + private static int compareTypeThenDist(InfoWrapper a, InfoWrapper b) { + if (a.getOutput() != b.getOutput()) return a.getOutput() ? 1 : -1; // Inputs First + + return getDistance(a) > getDistance(b) ? 1 : -1; // Furthest Last + } + + private static double getDistance(InfoWrapper info) { + return ((AccessibleInfoWrapper) (Object) (info)).labs$getDistance(); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/GuiAdvancedMemoryCardMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/GuiAdvancedMemoryCardMixin.java index f8023dff..2e75bb90 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/GuiAdvancedMemoryCardMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/GuiAdvancedMemoryCardMixin.java @@ -1,5 +1,7 @@ package com.nomiceu.nomilabs.mixin.betterp2p; +import net.minecraft.client.gui.GuiScreen; + import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -9,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.nomiceu.nomilabs.integration.betterp2p.AccessibleGuiAdvancedMemoryCard; +import com.nomiceu.nomilabs.integration.betterp2p.AccessibleInfoList; import com.nomiceu.nomilabs.integration.betterp2p.LabsClientCache; import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard; import com.projecturanus.betterp2p.client.gui.InfoList; @@ -19,10 +22,11 @@ import kotlin.Pair; /** - * Allows accessing needed functions and fields. Also fills up LabsClientCache. + * Allows accessing needed functions and fields, and initializes playerPos field in InfoList. Also fills up + * LabsClientCache. */ @Mixin(value = GuiAdvancedMemoryCard.class, remap = false) -public abstract class GuiAdvancedMemoryCardMixin implements AccessibleGuiAdvancedMemoryCard { +public abstract class GuiAdvancedMemoryCardMixin extends GuiScreen implements AccessibleGuiAdvancedMemoryCard { @Shadow private BetterMemoryCardModes mode; @@ -65,6 +69,11 @@ public abstract class GuiAdvancedMemoryCardMixin implements AccessibleGuiAdvance typeSelector.setVisible(false); } + @Inject(method = "initGui", at = @At("HEAD")) + private void setupInfoListPlayerPos(CallbackInfo ci) { + ((AccessibleInfoList) (Object) infos).labs$setPlayerPos(mc.player.getPositionVector()); + } + @Inject(method = "refreshOverlay", at = @At("HEAD")) private void fillLabsCache(CallbackInfo ci) { LabsClientCache.inputLoc.clear(); diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoListMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoListMixin.java new file mode 100644 index 00000000..d9b849fd --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoListMixin.java @@ -0,0 +1,115 @@ +package com.nomiceu.nomilabs.mixin.betterp2p; + +import java.util.Collection; +import java.util.HashMap; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +import net.minecraft.util.math.Vec3d; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.google.common.collect.ImmutableList; +import com.nomiceu.nomilabs.integration.betterp2p.AccessibleInfoList; +import com.nomiceu.nomilabs.integration.betterp2p.AccessibleInfoWrapper; +import com.nomiceu.nomilabs.integration.betterp2p.SortModes; +import com.projecturanus.betterp2p.client.gui.InfoFilter; +import com.projecturanus.betterp2p.client.gui.InfoList; +import com.projecturanus.betterp2p.client.gui.InfoWrapper; +import com.projecturanus.betterp2p.client.gui.widget.WidgetScrollBar; +import com.projecturanus.betterp2p.network.data.P2PLocation; + +/** + * Handles updating infos' distance to player, and allows for custom sorting. + */ +@Mixin(value = InfoList.class, remap = false) +public abstract class InfoListMixin implements AccessibleInfoList { + + @Shadow + @Final + private HashMap masterMap; + + @Shadow + @Final + private InfoFilter filter; + + @Shadow + @Nullable + public abstract InfoWrapper getSelectedInfo(); + + @Shadow + protected abstract String getSearchStr(); + + @Unique + private Vec3d labs$playerPos; + + @Unique + @Override + public void labs$setPlayerPos(Vec3d pos) { + labs$playerPos = pos; + calcDistFor(masterMap.values()); + } + + @Inject(method = "rebuild", at = @At("HEAD")) + private void calcDistanceInRebuild(Collection updateList, WidgetScrollBar scrollbar, int numEntries, + CallbackInfo ci) { + calcDistFor(updateList); + } + + @Inject(method = "update", at = @At("HEAD")) + private void calcDistanceInUpdate(Collection updateList, WidgetScrollBar scrollbar, int numEntries, + CallbackInfo ci) { + calcDistFor(updateList); + } + + @Unique + private void calcDistFor(Iterable infos) { + for (InfoWrapper info : infos) { + ((AccessibleInfoWrapper) (Object) info).labs$calculateDistance(labs$playerPos); + } + } + + @Inject(method = "resort", at = @At("HEAD"), cancellable = true) + private void customSortLogic(CallbackInfo ci) { + labs$getThis().getSorted().sort(SortModes.DEFAULT.getComp(getSelectedInfo())); + ci.cancel(); + } + + @Inject(method = "refilter", + at = @At(value = "HEAD"), + cancellable = true) + private void newFilterLogic(CallbackInfo ci) { + ci.cancel(); + + // Shortcut + String toSearch = getSearchStr().trim(); + if (toSearch.isEmpty()) { + labs$getThis().setFiltered(ImmutableList.copyOf(labs$getThis().getSorted())); + return; + } + + filter.updateFilter(toSearch.toLowerCase()); + labs$getThis().setFiltered(labs$getThis().getSorted().stream() + .filter(info -> { + if (getSelectedInfo() != null && info.getLoc().equals(getSelectedInfo().getLoc())) return true; + + for (var entry : filter.getActiveFilters().entrySet()) { + if (!entry.getKey().getFilter().invoke(info, entry.getValue())) return false; + } + return true; + }).sorted(SortModes.DEFAULT.getComp(getSelectedInfo())) + .collect(Collectors.toList())); + } + + @Unique + private InfoList labs$getThis() { + return (InfoList) (Object) this; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoWrapperMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoWrapperMixin.java new file mode 100644 index 00000000..da02e496 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/InfoWrapperMixin.java @@ -0,0 +1,79 @@ +package com.nomiceu.nomilabs.mixin.betterp2p; + +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextFormatting; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.nomiceu.nomilabs.integration.betterp2p.AccessibleInfoWrapper; +import com.projecturanus.betterp2p.client.gui.InfoWrapper; +import com.projecturanus.betterp2p.network.data.P2PInfo; +import com.projecturanus.betterp2p.network.data.P2PLocation; + +/** + * Allows saving of each P2P's distance to the player. + */ +@Mixin(value = InfoWrapper.class, remap = false) +public class InfoWrapperMixin implements AccessibleInfoWrapper { + + @Shadow + @Final + private P2PLocation loc; + + @Unique + private double labs$distanceToPlayer = 0.0; + + @Unique + @Override + public double labs$getDistance() { + return labs$distanceToPlayer; + } + + @Unique + @Override + public void labs$calculateDistance(Vec3d playerPos) { + // Change X, Y and Z Positions Based on Facing + + // Add 0.5 (middle of block) + double x = loc.getPos().getX() + 0.5; + double y = loc.getPos().getY() + 0.5; + double z = loc.getPos().getZ() + 0.5; + + // Amt to add/subtract base on facing. Since P2P is 2 pixels wide (1/8 of block), + // this leaves 1/16 from the half (middle of P2P) + double mod = 0.4375; + + switch (loc.getFacing()) { + case NORTH -> z -= mod; + case SOUTH -> z += mod; + case WEST -> x -= mod; + case EAST -> x += mod; + case UP -> y += mod; + case DOWN -> y -= mod; + } + + double distance = Math.sqrt(playerPos.squareDistanceTo(x, y, z)); + + // Round to 1dp + labs$distanceToPlayer = Math.round(distance * 10) / 10.0; + } + + @Inject(method = "", at = @At(value = "TAIL")) + private void provideChannelInfo(P2PInfo info, CallbackInfo ci) { + var channels = labs$getThis().getChannels(); + if (channels != null) + // Index 0-3: Default Info, 4+, Bound/Unbound, Offline/Online + labs$getThis().getHoverInfo().add(4, TextFormatting.LIGHT_PURPLE + channels); + } + + @Unique + private InfoWrapper labs$getThis() { + return ((InfoWrapper) (Object) this); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/WidgetP2PDeviceMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/WidgetP2PDeviceMixin.java index 5fd64645..73f3b4da 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/WidgetP2PDeviceMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/betterp2p/WidgetP2PDeviceMixin.java @@ -1,5 +1,7 @@ package com.nomiceu.nomilabs.mixin.betterp2p; +import javax.annotation.Nullable; + import net.minecraft.client.gui.GuiScreen; import org.spongepowered.asm.mixin.Final; @@ -10,24 +12,21 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import com.llamalad7.mixinextras.sugar.Local; import com.nomiceu.nomilabs.integration.betterp2p.AccessibleGuiAdvancedMemoryCard; +import com.nomiceu.nomilabs.integration.betterp2p.AccessibleInfoWrapper; import com.nomiceu.nomilabs.integration.betterp2p.ExtendedITypeReceiver; import com.nomiceu.nomilabs.integration.betterp2p.LabsBetterMemoryCardModes; import com.nomiceu.nomilabs.network.LabsNetworkHandler; import com.nomiceu.nomilabs.network.LabsP2PChangeTypeMessage; +import com.nomiceu.nomilabs.util.LabsTranslate; import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard; import com.projecturanus.betterp2p.client.gui.InfoWrapper; import com.projecturanus.betterp2p.client.gui.widget.P2PEntryConstants; import com.projecturanus.betterp2p.client.gui.widget.WidgetP2PDevice; -import com.projecturanus.betterp2p.item.BetterMemoryCardModes; - -import kotlin.jvm.functions.Function0; /** - * Changes Background colors, handles button visibilities for new modes. + * Changes Background colors, handles button visibilities for new modes, renders distance information. */ @Mixin(value = WidgetP2PDevice.class, remap = false) public abstract class WidgetP2PDeviceMixin implements ExtendedITypeReceiver { @@ -39,14 +38,12 @@ public abstract class WidgetP2PDeviceMixin implements ExtendedITypeReceiver { @Final private GuiAdvancedMemoryCard gui; - @Shadow - @Final - private Function0 infoSupplier; - @Shadow private int y; + @Shadow private int x; + @Unique private static final int LABS_INPUT_COLOR = 0x7f6d9cf8; @@ -58,7 +55,7 @@ public abstract class WidgetP2PDeviceMixin implements ExtendedITypeReceiver { target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V", remap = true)) private void drawNewBackgrounds(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { - var info = infoSupplier.invoke(); + var info = labs$getThis().getInfoSupplier().invoke(); boolean hasSelected = getSelectedInfo() != null; if (hasSelected && getSelectedInfo().getLoc() == info.getLoc()) { @@ -79,9 +76,31 @@ private void drawNewBackgrounds(int mouseX, int mouseY, float partialTicks, Call at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiScreen;drawRect(IIIII)V", remap = true)) private void cancelOldBackgroundDraws(int left, int top, int right, int bottom, int color) {} - @Inject(method = "updateButtonVisibility", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void handleAddInputVisibility(CallbackInfo ci, @Local(ordinal = 0) InfoWrapper info, - @Local(ordinal = 0) BetterMemoryCardModes mode) { + @Redirect(method = "render", + at = @At(value = "INVOKE", + target = "Lcom/projecturanus/betterp2p/client/gui/InfoWrapper;getChannels()Ljava/lang/String;"), + require = 1) + @Nullable + private String cancelExistingChannelHandling(InfoWrapper instance) { + return null; + } + + @Inject(method = "render", + at = @At(value = "INVOKE", + target = "Lcom/projecturanus/betterp2p/client/gui/widget/WidgetP2PDevice;updateButtonVisibility()V"), + require = 1) + private void renderDistance(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + var accessibleInfo = ((AccessibleInfoWrapper) (Object) labs$getThis().getInfoSupplier().invoke()); + // noinspection DataFlowIssue + gui.mc.fontRenderer.drawString(LabsTranslate.translate("nomilabs.gui.advanced_memory_card.info.dist", + accessibleInfo.labs$getDistance()), + x + P2PEntryConstants.LEFT_ALIGN, y + 33, 0); + } + + @Inject(method = "updateButtonVisibility", at = @At("TAIL")) + private void handleAddInputVisibility(CallbackInfo ci) { + var info = labs$getThis().getInfoSupplier().invoke(); + var mode = labs$getThis().getModeSupplier().invoke(); if (info == null || getSelectedInfo() == null) return; if (mode == LabsBetterMemoryCardModes.ADD_AS_INPUT || mode == LabsBetterMemoryCardModes.ADD_AS_OUTPUT) { // Bind Button should be visible if the device is not the selected, @@ -96,10 +115,15 @@ private void handleAddInputVisibility(CallbackInfo ci, @Local(ordinal = 0) InfoW @Override public void acceptIsInput(boolean isInput) { - var info = infoSupplier.invoke(); + var info = labs$getThis().getInfoSupplier().invoke(); if (info != null) { LabsNetworkHandler.NETWORK_HANDLER.sendToServer(new LabsP2PChangeTypeMessage(info.getLoc(), isInput)); } ((AccessibleGuiAdvancedMemoryCard) (Object) gui).labs$closeTypeSelector(); } + + @Unique + private WidgetP2PDevice labs$getThis() { + return (WidgetP2PDevice) (Object) this; + } } diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/jei/GuiIngredientMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/jei/GuiIngredientMixin.java index 9c50295a..4a59cd18 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/jei/GuiIngredientMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/jei/GuiIngredientMixin.java @@ -23,7 +23,7 @@ public class GuiIngredientMixin { @Inject(method = "drawTooltip", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"), - locals = LocalCapture.CAPTURE_FAILHARD) + locals = LocalCapture.CAPTURE_FAILEXCEPTION) private void addNewLine(Minecraft minecraft, int xOffset, int yOffset, int mouseX, int mouseY, T value, CallbackInfo ci, @Local List tooltip) { if (LabsConfig.modIntegration.addJEIIngEmptyLine) diff --git a/src/main/resources/assets/nomilabs/lang/en_us.lang b/src/main/resources/assets/nomilabs/lang/en_us.lang index 59816e0d..46b2bbc6 100644 --- a/src/main/resources/assets/nomilabs/lang/en_us.lang +++ b/src/main/resources/assets/nomilabs/lang/en_us.lang @@ -160,6 +160,8 @@ nomilabs.gui.better_p2p.input=Input nomilabs.gui.better_p2p.output=Output nomilabs.gui.better_p2p.error.same_output=P2P is already an {}. +nomilabs.gui.advanced_memory_card.info.dist=Distance: %sm + gui.advanced_memory_card.mode.add_as_input=Current Mode: Add as Input nomilabs.gui.advanced_memory_card.mode.add_input.desc.1=Before binding, the §aselected P2P§7 has its connections removed. nomilabs.gui.advanced_memory_card.mode.add_input.desc.2=The §aselected P2P§7 is set as an §9input§7, with the §bbind target's§7 frequency. diff --git a/src/main/resources/mixins.nomilabs.betterp2p.json b/src/main/resources/mixins.nomilabs.betterp2p.json index dd3ca38d..eef8c681 100644 --- a/src/main/resources/mixins.nomilabs.betterp2p.json +++ b/src/main/resources/mixins.nomilabs.betterp2p.json @@ -8,12 +8,14 @@ "BetterMemoryCardModesMixin", "CommonProxyMixin", "GridServerCacheMixin", + "InfoListMixin", "ServerRenameP2PTunnelMixin" ], "client": [ "ClientProxyMixin", "GuiAdvancedMemoryCardKtAccessor", "GuiAdvancedMemoryCardMixin", + "InfoWrapperMixin", "ModeWidgetButtonMixin", "RenderHandlerMixin", "WidgetP2PColumnMixin",