Skip to content

Commit e1afcac

Browse files
committed
Update Wurst Options, OppStats, Other Small Changes
1 parent 7f1bff5 commit e1afcac

6 files changed

Lines changed: 226 additions & 24 deletions

File tree

src/main/java/net/wurstclient/command/Command.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
package net.wurstclient.command;
99

10+
import java.util.Locale;
1011
import java.util.Objects;
1112

1213
import net.wurstclient.Category;
@@ -78,6 +79,34 @@ public final String[] getSyntax()
7879
return syntax;
7980
}
8081

82+
public boolean shouldSuggestPlayerNames(int argIndex)
83+
{
84+
if(argIndex < 0)
85+
return false;
86+
87+
for(String line : syntax)
88+
{
89+
if(line == null)
90+
continue;
91+
92+
String trimmed = line.trim();
93+
if(trimmed.regionMatches(true, 0, "Syntax:", 0, "Syntax:".length()))
94+
trimmed = trimmed.substring("Syntax:".length()).trim();
95+
if(!trimmed.startsWith("."))
96+
continue;
97+
98+
String[] tokens = trimmed.split("\\s+");
99+
if(tokens.length <= argIndex + 1)
100+
continue;
101+
102+
String token = tokens[argIndex + 1].toLowerCase(Locale.ROOT);
103+
if(token.contains("<player>"))
104+
return true;
105+
}
106+
107+
return false;
108+
}
109+
81110
public final void printHelp()
82111
{
83112
for(String line : description.split("\n"))

src/main/java/net/wurstclient/hacks/oppstats/OppStatsScreen.java

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.UUID;
1212
import java.util.ArrayList;
1313
import java.util.Optional;
14+
import java.util.Locale;
1415
import java.util.concurrent.CompletableFuture;
1516
import java.util.concurrent.ConcurrentHashMap;
1617
import java.util.concurrent.ConcurrentMap;
@@ -19,6 +20,7 @@
1920
import net.minecraft.client.Minecraft;
2021
import net.minecraft.client.gui.GuiGraphicsExtractor;
2122
import net.minecraft.client.gui.components.Button;
23+
import net.minecraft.client.gui.components.EditBox;
2224
import net.minecraft.client.gui.screens.Screen;
2325
import net.minecraft.client.input.KeyEvent;
2426
import net.minecraft.client.input.MouseButtonEvent;
@@ -45,11 +47,13 @@ public final class OppStatsScreen extends Screen
4547
private final Screen previous;
4648
private final OppStatsHack hack;
4749
private OppList list;
50+
private EditBox searchBox;
4851
private Button onlineButton;
4952
private Button historicalButton;
5053
private Button copyButton;
5154
private Button copyEventsButton;
5255
private boolean showOnline = true;
56+
private String searchQuery = "";
5357
private long nextReloadAt;
5458
private int infoScroll;
5559
private int infoMaxScroll;
@@ -68,25 +72,37 @@ public OppStatsScreen(Screen previous, OppStatsHack hack)
6872
@Override
6973
protected void init()
7074
{
71-
int top = 40;
75+
int top = 68;
7276
int bottomPad = 44;
7377
int listHeight = height - top - bottomPad;
78+
int leftPanelX = 16;
79+
int leftPanelW = Math.min(440, Math.max(220, width / 2 - 36));
7480
list = new OppList(Minecraft.getInstance(), width / 2 - 20, listHeight,
75-
top, 24, hack, showOnline);
81+
top, 24, hack, showOnline, searchQuery);
7682
addWidget(list);
7783

7884
addRenderableWidget(
7985
onlineButton = Button.builder(Component.literal("Online"), b -> {
8086
showOnline = true;
81-
list.reload(showOnline);
87+
list.reload(showOnline, searchQuery);
8288
updateModeButtonLabels();
8389
}).bounds(16, 12, 100, 20).build());
8490
addRenderableWidget(historicalButton =
8591
Button.builder(Component.literal("Historical"), b -> {
8692
showOnline = false;
87-
list.reload(showOnline);
93+
list.reload(showOnline, searchQuery);
8894
updateModeButtonLabels();
8995
}).bounds(122, 12, 110, 20).build());
96+
searchBox = new EditBox(font, leftPanelX, 36, leftPanelW, 18,
97+
Component.literal("Search"));
98+
searchBox.setHint(Component.literal("Search players..."));
99+
searchBox.setValue(searchQuery);
100+
searchBox.setResponder(value -> {
101+
searchQuery = value == null ? "" : value.trim();
102+
if(list != null)
103+
list.reload(showOnline, searchQuery);
104+
});
105+
addRenderableWidget(searchBox);
90106
copyButton = addRenderableWidget(Button
91107
.builder(Component.literal("Copy Profile"), b -> copySelected())
92108
.bounds(width - 360, height - 32, 110, 20).build());
@@ -105,7 +121,7 @@ public void tick()
105121
super.tick();
106122
if(System.currentTimeMillis() >= nextReloadAt)
107123
{
108-
list.reload(showOnline);
124+
list.reload(showOnline, searchQuery);
109125
nextReloadAt = System.currentTimeMillis() + 700L;
110126
}
111127
updateModeButtonLabels();
@@ -169,10 +185,13 @@ public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick)
169185
@Override
170186
public boolean keyPressed(KeyEvent context)
171187
{
172-
if(context.key() == GLFW.GLFW_KEY_UP)
173-
return list != null && list.moveSelection(-1);
174-
if(context.key() == GLFW.GLFW_KEY_DOWN)
175-
return list != null && list.moveSelection(1);
188+
if(searchBox == null || !searchBox.isFocused())
189+
{
190+
if(context.key() == GLFW.GLFW_KEY_UP)
191+
return list != null && list.moveSelection(-1);
192+
if(context.key() == GLFW.GLFW_KEY_DOWN)
193+
return list != null && list.moveSelection(1);
194+
}
176195

177196
if(super.keyPressed(context))
178197
return true;
@@ -326,7 +345,7 @@ private void drawInfoPanel(GuiGraphicsExtractor context, OppRecord selected,
326345
lines.add(new InfoLine("Status", true));
327346
lines.add(new InfoLine("HP: " + fmt(selected.health) + " Abs: "
328347
+ fmt(selected.absorption) + " Armor: " + na(selected.armorValue)
329-
+ " Ping: " + na(selected.ping), false));
348+
+ " Ping: " + na(getLivePing(selected)), false));
330349
lines.add(new InfoLine("Gamemode: " + na(selected.gamemode), false));
331350
lines.add(new InfoLine("", false));
332351
lines.add(new InfoLine("Stats", true));
@@ -530,7 +549,7 @@ private String fmt(float v)
530549
return String.format("%.1f", v);
531550
}
532551

533-
private String na(int value)
552+
private static String na(int value)
534553
{
535554
return value < 0 ? "N/A" : String.valueOf(value);
536555
}
@@ -540,6 +559,19 @@ private String na(String value)
540559
return value == null || value.isBlank() ? "N/A" : value;
541560
}
542561

562+
private static int getLivePing(OppRecord rec)
563+
{
564+
Minecraft mc = Minecraft.getInstance();
565+
if(rec == null || rec.uuid == null || mc.getConnection() == null)
566+
return rec == null ? -1 : rec.ping;
567+
568+
PlayerInfo info = mc.getConnection().getPlayerInfo(rec.uuid);
569+
if(info != null)
570+
return info.getLatency();
571+
572+
return rec.ping;
573+
}
574+
543575
private Identifier resolveSkin(UUID uuid)
544576
{
545577
requestMojangSkin(uuid);
@@ -601,24 +633,31 @@ private static final class OppList
601633
private boolean showOnline;
602634

603635
public OppList(Minecraft mc, int width, int height, int top,
604-
int itemHeight, OppStatsHack hack, boolean showOnline)
636+
int itemHeight, OppStatsHack hack, boolean showOnline,
637+
String searchQuery)
605638
{
606639
super(mc, width, height, top, itemHeight);
607640
this.hack = hack;
608641
this.showOnline = showOnline;
609-
reload(showOnline);
642+
reload(showOnline, searchQuery);
610643
ensureSelection();
611644
}
612645

613-
void reload(boolean showOnline)
646+
void reload(boolean showOnline, String searchQuery)
614647
{
615648
this.showOnline = showOnline;
616649
var prev = captureState();
617650
clearEntries();
618651
List<OppRecord> src = showOnline ? hack.getOnlineRecords()
619652
: hack.getHistoricalRecords();
653+
String query = searchQuery == null ? ""
654+
: searchQuery.trim().toLowerCase(Locale.ROOT);
620655
for(OppRecord rec : src)
656+
{
657+
if(!query.isBlank() && !matchesSearch(rec, query))
658+
continue;
621659
addEntry(new Entry(this, rec));
660+
}
622661
if(!children().isEmpty())
623662
restoreState(prev);
624663
}
@@ -669,6 +708,21 @@ public int getRowWidth()
669708
return width - 20;
670709
}
671710

711+
private boolean matchesSearch(OppRecord rec, String query)
712+
{
713+
if(rec == null || query == null || query.isBlank())
714+
return true;
715+
716+
String name =
717+
rec.name == null ? "" : rec.name.toLowerCase(Locale.ROOT);
718+
if(name.contains(query))
719+
return true;
720+
721+
String uuid = rec.uuid == null ? ""
722+
: rec.uuid.toString().toLowerCase(Locale.ROOT);
723+
return uuid.contains(query);
724+
}
725+
672726
private static final class Entry
673727
extends MultiSelectEntryListWidget.Entry<Entry>
674728
{
@@ -705,9 +759,10 @@ public void extractContent(GuiGraphicsExtractor context, int mouseX,
705759
40, 8, 16, 16, 8, 8, 64, 64, 0xFFFFFFFF);
706760
context.text(Minecraft.getInstance().font, record.name, x + 24,
707761
y + 2, 0xFFFFFFFF, false);
708-
context.text(Minecraft.getInstance().font,
709-
record.online ? "online" : "offline", x + 24, y + 12,
710-
record.online ? 0xFF55FF55 : 0xFFFF7777, false);
762+
String pingText = record.online
763+
? "ping: " + na(getLivePing(record)) + " ms" : "offline";
764+
context.text(Minecraft.getInstance().font, pingText, x + 24,
765+
y + 12, record.online ? 0xFF55FF55 : 0xFFFF7777, false);
711766
}
712767

713768
private Identifier resolveSkin(UUID uuid)

src/main/java/net/wurstclient/mixin/CommandSuggestionsMixin.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
2424
import net.minecraft.client.gui.components.CommandSuggestions;
2525
import net.minecraft.client.gui.components.EditBox;
26+
import net.minecraft.client.multiplayer.PlayerInfo;
2627
import net.wurstclient.WurstClient;
2728
import net.wurstclient.command.Command;
2829
import net.wurstclient.hack.Hack;
@@ -45,6 +46,8 @@ private void onRefresh(CallbackInfo ci)
4546
input.getValue().substring(0, input.getCursorPosition());
4647
if(wurst$showWurstCommandSuggestions(draftMessage))
4748
return;
49+
if(wurst$showPlayerNameSuggestions(draftMessage))
50+
return;
4851

4952
AutoCompleteHack autoComplete =
5053
WurstClient.INSTANCE.getHax().autoCompleteHack;
@@ -135,6 +138,83 @@ private void onRefresh(CallbackInfo ci)
135138
return true;
136139
}
137140

141+
private boolean wurst$showPlayerNameSuggestions(String draftMessage)
142+
{
143+
if(draftMessage == null || draftMessage.isEmpty()
144+
|| draftMessage.startsWith("/"))
145+
return false;
146+
147+
String prefix = ".";
148+
try
149+
{
150+
prefix = WurstClient.INSTANCE.getOtfs().commandPrefixOtf
151+
.getPrefixSetting().getSelected().toString();
152+
}catch(Throwable ignored)
153+
{}
154+
155+
if(prefix == null || prefix.isEmpty()
156+
|| !draftMessage.startsWith(prefix))
157+
return false;
158+
159+
String raw = draftMessage.substring(prefix.length()).stripLeading();
160+
if(raw.isEmpty())
161+
return false;
162+
163+
String[] tokens = raw.split("\\s+", -1);
164+
if(tokens.length < 2)
165+
return false;
166+
167+
String cmdName = tokens[0];
168+
Command cmd = WurstClient.INSTANCE.getCmds().getCmdByName(cmdName);
169+
if(cmd == null)
170+
return false;
171+
172+
int currentTokenIndex = tokens.length - 1;
173+
int argIndex = currentTokenIndex - 1;
174+
if(!cmd.shouldSuggestPlayerNames(argIndex))
175+
return false;
176+
177+
String currentToken = tokens[currentTokenIndex];
178+
String lowerToken = currentToken.toLowerCase(Locale.ROOT);
179+
int tokenStart = draftMessage.length() - currentToken.length();
180+
SuggestionsBuilder builder =
181+
new SuggestionsBuilder(draftMessage, tokenStart);
182+
LinkedHashSet<String> candidates = new LinkedHashSet<>();
183+
String inlineSuggestion = "";
184+
185+
if(WurstClient.MC.getConnection() != null)
186+
for(PlayerInfo info : WurstClient.MC.getConnection()
187+
.getOnlinePlayers())
188+
{
189+
if(info == null || info.getProfile() == null
190+
|| info.getProfile().name() == null)
191+
continue;
192+
193+
String name = info.getProfile().name();
194+
if(!lowerToken.isEmpty()
195+
&& !name.toLowerCase(Locale.ROOT).startsWith(lowerToken))
196+
continue;
197+
198+
candidates.add(name);
199+
}
200+
201+
for(String candidate : candidates)
202+
{
203+
builder.suggest(candidate);
204+
if(inlineSuggestion.isEmpty()
205+
&& candidate.length() > currentToken.length())
206+
inlineSuggestion = candidate.substring(currentToken.length());
207+
}
208+
209+
if(candidates.isEmpty())
210+
return false;
211+
212+
input.setSuggestion(inlineSuggestion);
213+
pendingSuggestions = builder.buildFuture();
214+
showSuggestions(false);
215+
return true;
216+
}
217+
138218
@Shadow
139219
public abstract void showSuggestions(boolean narrateFirstSuggestion);
140220
}

src/main/java/net/wurstclient/mixin/JoinMultiplayerScreenMixin.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,16 @@ private void onRefreshWidgetPositions(CallbackInfo ci)
261261
return;
262262
}
263263

264-
wurst$setCustomMultiplayerUiVisible(true);
265-
266-
wurst$layoutServerPanels();
267-
wurst$ensurePanelWidgets();
268-
wurst$ensureSearchWidgets();
269-
wurst$layoutSearchWidgets();
270-
wurst$updateSearchResults();
264+
boolean customLayout = wurst$shouldUseCustomMultiplayerLayout();
265+
wurst$setCustomMultiplayerUiVisible(customLayout);
266+
if(customLayout)
267+
{
268+
wurst$layoutServerPanels();
269+
wurst$ensurePanelWidgets();
270+
wurst$ensureSearchWidgets();
271+
wurst$layoutSearchWidgets();
272+
wurst$updateSearchResults();
273+
}
271274

272275
if(NiceWurstModule.showAltManager())
273276
{
@@ -466,6 +469,17 @@ private void onRefreshWidgetPositions(CallbackInfo ci)
466469
}
467470
}
468471

472+
@Unique
473+
private boolean wurst$shouldUseCustomMultiplayerLayout()
474+
{
475+
if(WurstClient.INSTANCE.getOtfs() == null
476+
|| WurstClient.INSTANCE.getOtfs().wurstOptionsOtf == null)
477+
return true;
478+
479+
return WurstClient.INSTANCE.getOtfs().wurstOptionsOtf
480+
.isCustomMultiplayerLayoutEnabled();
481+
}
482+
469483
@Inject(method = "join(Lnet/minecraft/client/multiplayer/ServerData;)V",
470484
at = @At("HEAD"))
471485
private void onConnect(ServerData entry, CallbackInfo ci)

0 commit comments

Comments
 (0)