diff --git a/README.md b/README.md index 5130ef4..9719e8c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,11 @@ Logo made by [CarbonGhost](https://github.com/CarbonGhost) ## Info -This mod adds search bars into the resource packs list +This mod adds more search bars into the UI: + +- Resource pack selector screen +- Datapack selector screen +- Multiplayer screen ## Discord diff --git a/src/main/java/net/onpointcoding/enhancedsearchability/duck/PackListWidgetDuckProvider.java b/src/main/java/net/onpointcoding/enhancedsearchability/duck/ListWidgetDuckProvider.java similarity index 55% rename from src/main/java/net/onpointcoding/enhancedsearchability/duck/PackListWidgetDuckProvider.java rename to src/main/java/net/onpointcoding/enhancedsearchability/duck/ListWidgetDuckProvider.java index ebce645..b9fc4fa 100644 --- a/src/main/java/net/onpointcoding/enhancedsearchability/duck/PackListWidgetDuckProvider.java +++ b/src/main/java/net/onpointcoding/enhancedsearchability/duck/ListWidgetDuckProvider.java @@ -1,17 +1,20 @@ package net.onpointcoding.enhancedsearchability.duck; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; import net.minecraft.client.gui.screen.pack.PackListWidget; import net.minecraft.text.Text; import java.util.List; import java.util.function.Supplier; -public interface PackListWidgetDuckProvider { +public interface ListWidgetDuckProvider { Text getHeaderText(); void filter(Supplier searchTextSupplier); - List getSyncStore(); + List getSyncStoreRP(); + + List getSyncStoreServer(); void hideHeaderAndShift(); } diff --git a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerScreen.java b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerScreen.java new file mode 100644 index 0000000..97fab35 --- /dev/null +++ b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerScreen.java @@ -0,0 +1,65 @@ +package net.onpointcoding.enhancedsearchability.mixin; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import net.onpointcoding.enhancedsearchability.duck.ListWidgetDuckProvider; +import net.onpointcoding.enhancedsearchability.utils.ClearableTextFieldDual; +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(MultiplayerScreen.class) +public class MixinMultiplayerScreen extends Screen { + @Shadow + protected MultiplayerServerListWidget serverListWidget; + @Shadow + private boolean initialized; + private TextFieldWidget serverSearchBox; + private ButtonWidget serverClearButton; + + protected MixinMultiplayerScreen(Text title) { + super(title); + } + + @Inject(method = "init", at = @At("TAIL")) + private void injected_init(CallbackInfo ci) { + MinecraftClient mc = MinecraftClient.getInstance(); + ClearableTextFieldDual a = addSearchBox(mc, this.serverListWidget, this.serverSearchBox); + this.serverSearchBox = a.getTextFieldWidget(); + this.serverClearButton = a.getClearButton(); + + if (this.initialized) + setupOriginalServerListOffset(this.serverListWidget); + } + + void setupOriginalServerListOffset(MultiplayerServerListWidget multiplayerServerListWidget) { + if (multiplayerServerListWidget instanceof ListWidgetDuckProvider duck) + duck.hideHeaderAndShift(); + } + + ClearableTextFieldDual addSearchBox(MinecraftClient mc, MultiplayerServerListWidget serverListWidget, TextFieldWidget textFieldWidget) { + textFieldWidget = new TextFieldWidget(mc.textRenderer, serverListWidget.getRowLeft() - 1, 34, serverListWidget.getRowWidth() - 22, 18, textFieldWidget, new TranslatableText("enhancedsearchability.searchbox")); + textFieldWidget.setChangedListener((search) -> { + if (serverListWidget instanceof ListWidgetDuckProvider duckProvider) + duckProvider.filter(() -> search); + }); + this.addSelectableChild(textFieldWidget); + TextFieldWidget finalTextFieldWidget = textFieldWidget; + ButtonWidget clearButton = this.addDrawableChild(new ButtonWidget(serverListWidget.getRowLeft() + serverListWidget.getRowWidth() - 22, 33, 20, 20, new TranslatableText("enhancedsearchability.clearbutton"), buttonWidget -> finalTextFieldWidget.setText(""))); + return new ClearableTextFieldDual(textFieldWidget, clearButton); + } + + @Inject(method = "render", at = @At("TAIL")) + private void injected_render(MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) { + this.serverSearchBox.render(matrices, mouseX, mouseY, delta); + } +} diff --git a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerServerListWidget.java b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerServerListWidget.java new file mode 100644 index 0000000..aec6cf8 --- /dev/null +++ b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinMultiplayerServerListWidget.java @@ -0,0 +1,112 @@ +package net.onpointcoding.enhancedsearchability.mixin; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; +import net.minecraft.client.gui.screen.pack.PackListWidget; +import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; +import net.minecraft.text.Text; +import net.onpointcoding.enhancedsearchability.duck.ListWidgetDuckProvider; +import org.objectweb.asm.Opcodes; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.Supplier; +import java.util.stream.Stream; + +@Mixin(MultiplayerServerListWidget.class) +public class MixinMultiplayerServerListWidget extends AlwaysSelectedEntryListWidget implements ListWidgetDuckProvider { + @Shadow + @Final + private MultiplayerScreen screen; + @Shadow + @Final + private List lanServers; + @Shadow + @Final + private MultiplayerServerListWidget.Entry scanningEntry; + private final List serverSyncStore = new ArrayList<>(); + private final List lanServerSyncStore = new ArrayList<>(); + private Supplier searchTextStore = () -> ""; + + public MixinMultiplayerServerListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) { + super(minecraftClient, i, j, k, l, m); + } + + @Override + public Text getHeaderText() { + return null; + } + + @Override + public void filter(Supplier searchTextSupplier) { + searchTextStore = searchTextSupplier; + customAddServerStreamToUI(this.serverSyncStore.stream(), this.lanServerSyncStore.stream(), searchTextStore); + } + + @Inject(method = "updateEntries", at = @At("TAIL")) + private void injected_updateEntries(CallbackInfo ci) { + customAddServerStreamToUI(this.serverSyncStore.stream(), this.lanServerSyncStore.stream(), searchTextStore); + } + + @Redirect(method = "setServers", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/multiplayer/MultiplayerServerListWidget;servers:Ljava/util/List;", opcode = Opcodes.GETFIELD)) + private List redirectServersList(MultiplayerServerListWidget instance) { + return serverSyncStore; + } + + @Redirect(method = "setLanServers", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/multiplayer/MultiplayerServerListWidget;lanServers:Ljava/util/List;", opcode = Opcodes.GETFIELD)) + private List redirectLanServersList(MultiplayerServerListWidget instance) { + return lanServerSyncStore; + } + + private void customAddServerStreamToUI(Stream serverStream, Stream lanServerStream, Supplier searchTextSupplier) { + String s = searchTextSupplier.get().toLowerCase(Locale.ROOT); + boolean isEmpty = s.equals(""); + + this.children().clear(); + serverStream.forEach(serverEntry -> { + if (isEmpty || serverEntry.getServer().name.toLowerCase(Locale.ROOT).contains(s)) + this.children().add(serverEntry); + }); + this.children().add(this.scanningEntry); + lanServerStream.forEach(lanServerEntry -> { + if (isEmpty || isMatchingLanServer(lanServerEntry, s)) + this.children().add(lanServerEntry); + }); + } + + private boolean isMatchingLanServer(MultiplayerServerListWidget.LanServerEntry lanServerEntry, String s) { + return lanServerEntry.getLanServerEntry().getAddressPort().toLowerCase(Locale.ROOT).contains(s) + || lanServerEntry.getLanServerEntry().getMotd().toLowerCase(Locale.ROOT).contains(s); + } + + @Override + public List getSyncStoreRP() { + return null; + } + + @Override + public List getSyncStoreServer() { + return serverSyncStore; + } + + @Override + public void hideHeaderAndShift() { + this.top += 25; + } + + @Override + public double getScrollAmount() { + double v = super.getScrollAmount(); + int m = getMaxScroll(); + return v > m ? m : v; + } +} diff --git a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackListWidget.java b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackListWidget.java index 3695fc3..9f0d4e3 100644 --- a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackListWidget.java +++ b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackListWidget.java @@ -1,10 +1,11 @@ package net.onpointcoding.enhancedsearchability.mixin; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; import net.minecraft.client.gui.screen.pack.PackListWidget; import net.minecraft.client.gui.widget.EntryListWidget; import net.minecraft.text.Text; -import net.onpointcoding.enhancedsearchability.duck.PackListWidgetDuckProvider; +import net.onpointcoding.enhancedsearchability.duck.ListWidgetDuckProvider; import net.onpointcoding.enhancedsearchability.duck.ResourcePackEntryDuckProvider; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -18,7 +19,7 @@ import java.util.function.Supplier; @Mixin(PackListWidget.class) -public abstract class MixinPackListWidget extends EntryListWidget implements PackListWidgetDuckProvider { +public abstract class MixinPackListWidget extends EntryListWidget implements ListWidgetDuckProvider { @Shadow @Final private Text title; @@ -75,7 +76,12 @@ boolean hasMatchingName(PackListWidget.ResourcePackEntry child, String a) { } @Override - public List getSyncStore() { + public List getSyncStoreRP() { return storeChildren; } + + @Override + public List getSyncStoreServer() { + return null; + } } diff --git a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackScreen.java b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackScreen.java index 0f41e98..8125e87 100644 --- a/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackScreen.java +++ b/src/main/java/net/onpointcoding/enhancedsearchability/mixin/MixinPackScreen.java @@ -15,7 +15,7 @@ import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.Formatting; -import net.onpointcoding.enhancedsearchability.duck.PackListWidgetDuckProvider; +import net.onpointcoding.enhancedsearchability.duck.ListWidgetDuckProvider; import net.onpointcoding.enhancedsearchability.utils.ClearableTextFieldDual; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -68,14 +68,14 @@ private void injected_init(CallbackInfo ci) { } void setupOriginalPackListOffset(PackListWidget packListWidget) { - if (packListWidget instanceof PackListWidgetDuckProvider duck) + if (packListWidget instanceof ListWidgetDuckProvider duck) duck.hideHeaderAndShift(); } ClearableTextFieldDual addSearchBox(MinecraftClient mc, PackListWidget packListWidget, TextFieldWidget textFieldWidget, Text packListHeader, int leftPos) { textFieldWidget = new TextFieldWidget(mc.textRenderer, packListWidget.getRowLeft() - 1, 47, packListWidget.getRowWidth() - 22, 18, textFieldWidget, new TranslatableText("enhancedsearchability.searchbox")); textFieldWidget.setChangedListener((search) -> { - if (packListWidget instanceof PackListWidgetDuckProvider duckProvider) + if (packListWidget instanceof ListWidgetDuckProvider duckProvider) duckProvider.filter(() -> search); }); this.addSelectableChild(textFieldWidget); @@ -113,7 +113,7 @@ void renderOverlayHeader(MatrixStack matrices, MinecraftClient mc, PackListWidge bufferBuilder.vertex(left, top, 0).texture(left / 32f, top / 32f).color(32, 32, 32, 255).next(); tessellator.draw(); - Text text1 = packListWidget instanceof PackListWidgetDuckProvider duck ? duck.getHeaderText() : new LiteralText(""); + Text text1 = packListWidget instanceof ListWidgetDuckProvider duck ? duck.getHeaderText() : new LiteralText(""); Text text = (new LiteralText("")).append(text1).formatted(Formatting.UNDERLINE, Formatting.BOLD); mc.textRenderer.draw(matrices, text, (float) (left + w / 2 - mc.textRenderer.getWidth(text) / 2), 35, 0xffffff); } @@ -121,24 +121,24 @@ void renderOverlayHeader(MatrixStack matrices, MinecraftClient mc, PackListWidge @Inject(method = "updatePackLists", at = @At("HEAD"), cancellable = true) private void injected_updatePackLists(CallbackInfo ci) { if (this.client != null) { - if (this.availablePackList instanceof PackListWidgetDuckProvider duckProvider && this.availablePackSearchBox != null) { + if (this.availablePackList instanceof ListWidgetDuckProvider duckProvider && this.availablePackSearchBox != null) { customUpdatePackList(this.client, this.availablePackList, duckProvider, this.organizer.getDisabledPacks()); duckProvider.filter(() -> this.availablePackSearchBox.getText()); } - if (this.selectedPackList instanceof PackListWidgetDuckProvider duckProvider && this.selectedPackSearchBox != null) { + if (this.selectedPackList instanceof ListWidgetDuckProvider duckProvider && this.selectedPackSearchBox != null) { customUpdatePackList(this.client, this.selectedPackList, duckProvider, this.organizer.getEnabledPacks()); duckProvider.filter(() -> this.selectedPackSearchBox.getText()); - this.doneButton.active = !duckProvider.getSyncStore().isEmpty(); + this.doneButton.active = !duckProvider.getSyncStoreRP().isEmpty(); } ci.cancel(); } } - private void customUpdatePackList(MinecraftClient mc, PackListWidget widget, PackListWidgetDuckProvider duck, Stream packs) { - duck.getSyncStore().clear(); + private void customUpdatePackList(MinecraftClient mc, PackListWidget widget, ListWidgetDuckProvider duck, Stream packs) { + duck.getSyncStoreRP().clear(); packs.forEach((pack) -> { PackListWidget.ResourcePackEntry resourcePackEntry = new PackListWidget.ResourcePackEntry(mc, widget, this, pack); - duck.getSyncStore().add(resourcePackEntry); + duck.getSyncStoreRP().add(resourcePackEntry); }); } diff --git a/src/main/resources/EnhancedSearchability.mixins.json b/src/main/resources/EnhancedSearchability.mixins.json index ebb2bd5..351cc90 100644 --- a/src/main/resources/EnhancedSearchability.mixins.json +++ b/src/main/resources/EnhancedSearchability.mixins.json @@ -5,6 +5,8 @@ "compatibilityLevel": "JAVA_16", "mixins": [], "client": [ + "MixinMultiplayerScreen", + "MixinMultiplayerServerListWidget", "MixinPackListWidget", "MixinPackScreen", "MixinResourcePackEntry"