Skip to content
This repository has been archived by the owner on Jan 30, 2024. It is now read-only.

Commit

Permalink
Add search bar to the multiplayer server list
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmelon54 committed Oct 21, 2021
1 parent e8804f3 commit 0d39e7b
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 16 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> searchTextSupplier);

List<PackListWidget.ResourcePackEntry> getSyncStore();
List<PackListWidget.ResourcePackEntry> getSyncStoreRP();

List<MultiplayerServerListWidget.ServerEntry> getSyncStoreServer();

void hideHeaderAndShift();
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<MultiplayerServerListWidget.Entry> implements ListWidgetDuckProvider {
@Shadow
@Final
private MultiplayerScreen screen;
@Shadow
@Final
private List<MultiplayerServerListWidget.LanServerEntry> lanServers;
@Shadow
@Final
private MultiplayerServerListWidget.Entry scanningEntry;
private final List<MultiplayerServerListWidget.ServerEntry> serverSyncStore = new ArrayList<>();
private final List<MultiplayerServerListWidget.LanServerEntry> lanServerSyncStore = new ArrayList<>();
private Supplier<String> 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<String> 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<MultiplayerServerListWidget.ServerEntry> 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<MultiplayerServerListWidget.LanServerEntry> redirectLanServersList(MultiplayerServerListWidget instance) {
return lanServerSyncStore;
}

private void customAddServerStreamToUI(Stream<MultiplayerServerListWidget.ServerEntry> serverStream, Stream<MultiplayerServerListWidget.LanServerEntry> lanServerStream, Supplier<String> 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<PackListWidget.ResourcePackEntry> getSyncStoreRP() {
return null;
}

@Override
public List<MultiplayerServerListWidget.ServerEntry> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -18,7 +19,7 @@
import java.util.function.Supplier;

@Mixin(PackListWidget.class)
public abstract class MixinPackListWidget extends EntryListWidget<PackListWidget.ResourcePackEntry> implements PackListWidgetDuckProvider {
public abstract class MixinPackListWidget extends EntryListWidget<PackListWidget.ResourcePackEntry> implements ListWidgetDuckProvider {
@Shadow
@Final
private Text title;
Expand Down Expand Up @@ -75,7 +76,12 @@ boolean hasMatchingName(PackListWidget.ResourcePackEntry child, String a) {
}

@Override
public List<PackListWidget.ResourcePackEntry> getSyncStore() {
public List<PackListWidget.ResourcePackEntry> getSyncStoreRP() {
return storeChildren;
}

@Override
public List<MultiplayerServerListWidget.ServerEntry> getSyncStoreServer() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -113,32 +113,32 @@ 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);
}

@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<ResourcePackOrganizer.Pack> packs) {
duck.getSyncStore().clear();
private void customUpdatePackList(MinecraftClient mc, PackListWidget widget, ListWidgetDuckProvider duck, Stream<ResourcePackOrganizer.Pack> 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);
});
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/EnhancedSearchability.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"compatibilityLevel": "JAVA_16",
"mixins": [],
"client": [
"MixinMultiplayerScreen",
"MixinMultiplayerServerListWidget",
"MixinPackListWidget",
"MixinPackScreen",
"MixinResourcePackEntry"
Expand Down

0 comments on commit 0d39e7b

Please sign in to comment.