Skip to content
This repository was archived by the owner on Nov 28, 2025. It is now read-only.

Commit f73646e

Browse files
authored
Playerstats command (#139)
Implement the `/playerstats` command. - make API wrappers async & thread-safe - implement playerstats command for 1.8.9 - bundle brigadier client-side (on 1.8.9) to hopefully enable better abstraction cross-version - move class `LevelHead` -> module `common` since it doesn't depend on MC. TODO: - [x] split `HypixelAbstractionLayer` to two parts: one to handle API and the other to handle game/transient state - [ ] configurable cache size - [x] port to other versions - [x] clean up rate-limit logic - [x] implement for other games - [x] add pretty colors Non goals (for this PR): - Overlay
2 parents ebc9992 + 4260b66 commit f73646e

File tree

113 files changed

+3085
-625
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+3085
-625
lines changed

1.16_combat-6/src/main/java/io/github/axolotlclient/api/AddFriendScreen.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222

2323
package io.github.axolotlclient.api;
2424

25-
import java.util.UUID;
26-
2725
import io.github.axolotlclient.api.requests.FriendRequest;
2826
import io.github.axolotlclient.api.util.UUIDHelper;
2927
import io.github.axolotlclient.util.notifications.Notifications;
@@ -36,13 +34,7 @@ public AddFriendScreen(Screen parent) {
3634
super(parent, new TranslatableText("api.screen.friends.add"),
3735
new TranslatableText("api.screen.friends.add.name"), s -> {
3836
if (API.getInstance().isSocketConnected()) {
39-
String uuid;
40-
try {
41-
uuid = API.getInstance().sanitizeUUID(UUID.fromString(s).toString());
42-
} catch (IllegalArgumentException e) {
43-
uuid = UUIDHelper.getUuid(s);
44-
}
45-
FriendRequest.getInstance().addFriend(uuid);
37+
UUIDHelper.ensureUuid(s).thenAccept(FriendRequest.getInstance()::addFriend);
4638
} else {
4739
Notifications.getInstance().addStatus("api.error.notLoggedIn", "api.error.notLoggedIn.desc");
4840
}

1.16_combat-6/src/main/java/io/github/axolotlclient/api/chat/ChannelInvitesScreen.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
package io.github.axolotlclient.api.chat;
2424

25+
import java.util.concurrent.CompletableFuture;
26+
2527
import io.github.axolotlclient.AxolotlClientConfig.impl.ui.vanilla.widgets.PlainTextButtonWidget;
2628
import io.github.axolotlclient.api.requests.ChannelRequest;
2729
import io.github.axolotlclient.api.types.ChannelInvite;
@@ -105,16 +107,19 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) {
105107
private class InvitesListEntry extends Entry<InvitesListEntry> {
106108

107109
private final ChannelInvite invite;
110+
private final CompletableFuture<String> fromName;
108111

109112
public InvitesListEntry(ChannelInvite invite) {
110113
this.invite = invite;
114+
this.fromName = UUIDHelper.tryGetUsernameAsync(invite.fromUuid());
111115
}
112116

113117
@Override
114118
public void render(MatrixStack graphics, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean hovering, float partialTick) {
115119
drawTextWithShadow(graphics, textRenderer, new TranslatableText("api.channels.invite.name", invite.channelName()), left + 2, top + 2, -1);
116-
drawTextWithShadow(graphics, textRenderer, new TranslatableText("api.channels.invite.from", UUIDHelper.getUsername(invite.fromUuid())).setStyle(Style.EMPTY.withItalic(true)), left + 15, top + height - textRenderer.fontHeight - 1, 0x808080);
117-
120+
if (fromName.isDone()) {
121+
drawTextWithShadow(graphics, textRenderer, new TranslatableText("api.channels.invite.from", fromName.join()).setStyle(Style.EMPTY.withItalic(true)), left + 15, top + height - textRenderer.fontHeight - 1, 0x808080);
122+
}
118123
}
119124
}
120125
}

1.16_combat-6/src/main/java/io/github/axolotlclient/api/chat/CreateChannelScreen.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import io.github.axolotlclient.AxolotlClientConfig.impl.util.DrawUtil;
3434
import io.github.axolotlclient.api.requests.ChannelRequest;
3535
import io.github.axolotlclient.api.types.Persistence;
36-
import io.github.axolotlclient.api.util.UUIDHelper;
3736
import net.minecraft.client.gui.screen.Screen;
3837
import net.minecraft.client.gui.screen.ScreenTexts;
3938
import net.minecraft.client.gui.widget.AbstractButtonWidget;
@@ -148,7 +147,7 @@ public Persistence.Type getValue() {
148147
addButton(new ButtonWidget(width / 2 + 4, footerY, 150, 20, ScreenTexts.DONE, widget -> {
149148
ChannelRequest.createChannel(nameField.getText(),
150149
Persistence.of(persistence.getValue(), count.get().get(), duration.get().get()),
151-
Arrays.stream(namesInput.getText().split(",")).filter(s -> !s.isEmpty()).map(UUIDHelper::ensureUuid).toArray(String[]::new))
150+
Arrays.stream(namesInput.getText().split(",")).filter(s -> !s.isEmpty()).toArray(String[]::new))
152151
.thenRun(() -> client.execute(() -> client.openScreen(parent)));
153152
;
154153
}));
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright © 2024 moehreag <moehreag@gmail.com> & Contributors
3+
*
4+
* This file is part of AxolotlClient.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*
20+
* For more information, see the LICENSE file.
21+
*/
22+
23+
package io.github.axolotlclient.commands;
24+
25+
import java.util.Locale;
26+
import java.util.Objects;
27+
import java.util.Optional;
28+
import java.util.concurrent.CompletableFuture;
29+
import java.util.regex.Pattern;
30+
31+
import com.mojang.brigadier.StringReader;
32+
import com.mojang.brigadier.arguments.ArgumentType;
33+
import com.mojang.brigadier.context.CommandContext;
34+
import com.mojang.brigadier.suggestion.Suggestions;
35+
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
36+
import io.github.axolotlclient.api.util.UUIDHelper;
37+
import net.minecraft.client.MinecraftClient;
38+
import net.minecraft.client.network.ClientPlayNetworkHandler;
39+
import org.jetbrains.annotations.Nullable;
40+
41+
public class PlayerArgument implements ArgumentType<PlayerArgument.PlayerInfo> {
42+
public static final class PlayerInfo {
43+
private final String playerName;
44+
@Nullable
45+
private CompletableFuture<Optional<String>> uuid;
46+
47+
public PlayerInfo(String playerName) {
48+
this.playerName = playerName;
49+
}
50+
51+
public String playerName() {
52+
return playerName;
53+
}
54+
55+
public CompletableFuture<Optional<String>> uuid() {
56+
if (uuid == null) {
57+
uuid = UUIDHelper.USERNAME_TO_UUID.getAsync(playerName);
58+
}
59+
60+
return uuid;
61+
}
62+
63+
@Override
64+
public boolean equals(Object obj) {
65+
if (obj == this) return true;
66+
if (obj == null || obj.getClass() != this.getClass()) return false;
67+
var that = (PlayerInfo) obj;
68+
return Objects.equals(this.playerName, that.playerName) &&
69+
Objects.equals(this.uuid, that.uuid);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return Objects.hash(playerName, uuid);
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return "PlayerInfo[" +
80+
"playerName=" + playerName + ", " +
81+
"uuid=" + uuid + ']';
82+
}
83+
84+
}
85+
86+
private static final Pattern NAME_REGEX = Pattern.compile("[a-zA-Z0-9_]{2,16}");
87+
88+
@Override
89+
public PlayerInfo parse(StringReader stringReader) {
90+
String playerName = stringReader.readUnquotedString();
91+
return new PlayerInfo(playerName);
92+
}
93+
94+
@Override
95+
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
96+
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
97+
if (handler == null) {
98+
return builder.buildFuture();
99+
}
100+
101+
handler.getPlayerList()
102+
.stream()
103+
.map(playerInfo -> playerInfo.getProfile().getName())
104+
.filter(name -> NAME_REGEX.matcher(name).matches())
105+
.filter(name -> name.toLowerCase(Locale.ROOT).startsWith(builder.getRemaining().toLowerCase(Locale.ROOT)))
106+
.forEach(builder::suggest);
107+
108+
return builder.buildFuture();
109+
}
110+
111+
public static PlayerInfo get(CommandContext<?> context, String name) {
112+
return context.getArgument(name, PlayerInfo.class);
113+
}
114+
115+
public static PlayerArgument player() {
116+
return new PlayerArgument();
117+
}
118+
}

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/ClientWorldMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public abstract class ClientWorldMixin {
4343
Entity entity = this.getEntityById(entityId);
4444
if (entity instanceof PlayerEntity && HypixelMods.getInstance().cacheMode.get()
4545
.equals(HypixelMods.HypixelCacheMode.ON_PLAYER_DISCONNECT.toString())) {
46-
HypixelAbstractionLayer.handleDisconnectEvents(entity.getUuid());
46+
HypixelAbstractionLayer.getInstance().handleDisconnectEvents(entity.getUuid());
4747
}
4848
}
4949

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/EntityRendererMixin.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@
2525
import com.mojang.blaze3d.systems.RenderSystem;
2626
import io.github.axolotlclient.AxolotlClient;
2727
import io.github.axolotlclient.api.requests.UserRequest;
28-
import io.github.axolotlclient.modules.hypixel.HypixelAbstractionLayer;
2928
import io.github.axolotlclient.modules.hypixel.bedwars.BedwarsMod;
3029
import io.github.axolotlclient.modules.hypixel.levelhead.LevelHead;
31-
import io.github.axolotlclient.modules.hypixel.levelhead.LevelHeadMode;
3230
import io.github.axolotlclient.modules.hypixel.nickhider.NickHider;
3331
import io.github.axolotlclient.util.Util;
3432
import net.minecraft.client.MinecraftClient;
@@ -131,11 +129,7 @@ public abstract class EntityRendererMixin<T extends Entity> {
131129
light);
132130
}
133131
} else if (LevelHead.getInstance().enabled.get()) {
134-
String text = "Level: " + HypixelAbstractionLayer.getPlayerLevel(String.valueOf(entity.getUuid()), LevelHead.getInstance().mode.get());
135-
136-
if (LevelHead.getInstance().mode.get().equals(LevelHeadMode.BEDWARS)) {
137-
text += "☆";
138-
}
132+
String text = LevelHead.getInstance().getDisplayString(entity.getUuid().toString());
139133

140134
float x = -textRenderer.getWidth(text) / 2F;
141135
float y = string.getString().contains("deadmau5") ? -20 : -10;

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/GameMenuScreenMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public void addClientOptionsButton(Args args) {
8686
return (buttonWidget) -> {
8787
if (Objects.equals(HypixelMods.getInstance().cacheMode.get(),
8888
HypixelMods.HypixelCacheMode.ON_CLIENT_DISCONNECT.toString())) {
89-
HypixelAbstractionLayer.clearPlayerData();
89+
HypixelAbstractionLayer.getInstance().clearPlayerData();
9090
}
9191
onPress.onPress(buttonWidget);
9292
};

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/PlayerListHudMixin.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@
3030
import com.mojang.blaze3d.systems.RenderSystem;
3131
import io.github.axolotlclient.AxolotlClient;
3232
import io.github.axolotlclient.api.requests.UserRequest;
33-
import io.github.axolotlclient.modules.hypixel.HypixelAbstractionLayer;
33+
import io.github.axolotlclient.api.util.UUIDHelper;
3434
import io.github.axolotlclient.modules.hypixel.bedwars.BedwarsGame;
3535
import io.github.axolotlclient.modules.hypixel.bedwars.BedwarsMod;
3636
import io.github.axolotlclient.modules.hypixel.bedwars.BedwarsPlayer;
37+
import io.github.axolotlclient.modules.hypixel.levelhead.LevelHead;
3738
import io.github.axolotlclient.modules.hypixel.levelhead.LevelHeadMode;
3839
import io.github.axolotlclient.modules.hypixel.nickhider.NickHider;
3940
import io.github.axolotlclient.modules.tablist.Tablist;
@@ -181,9 +182,8 @@ private Text nickHider(PlayerListEntry entry, Operation<Text> original) {
181182
return;
182183
}
183184

184-
render = String.valueOf(HypixelAbstractionLayer.getPlayerLevel(playerListEntry2
185-
.getProfile().getId().toString().replace("-", ""),
186-
LevelHeadMode.BEDWARS));
185+
String uuid = UUIDHelper.toUndashed(playerListEntry2.getProfile().getId());
186+
render = LevelHead.getDisplayString(LevelHeadMode.BEDWARS, uuid);
187187
} catch (Exception e) {
188188
return;
189189
}

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/ReloadableResourceManagerMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public abstract class ReloadableResourceManagerMixin {
4747
@Inject(method = "beginMonitoredReload", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ReloadableResourceManagerImpl;beginReloadInner(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/List;Ljava/util/concurrent/CompletableFuture;)Lnet/minecraft/resource/ResourceReloadMonitor;"))
4848
public void axolotlclient$reload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture<Unit> initialStage,
4949
List<ResourcePack> packs, CallbackInfoReturnable<ResourceReloadMonitor> cir) {
50-
HypixelAbstractionLayer.clearPlayerData();
50+
HypixelAbstractionLayer.getInstance().clearPlayerData();
5151

5252
PackDisplayHud hud = (PackDisplayHud) HudManager.getInstance().get(PackDisplayHud.ID);
5353
if (hud != null) {

1.16_combat-6/src/main/java/io/github/axolotlclient/mixin/ScreenshotRecorderMixin.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,21 @@
2323
package io.github.axolotlclient.mixin;
2424

2525
import java.io.File;
26+
import java.util.function.Consumer;
2627

27-
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
28-
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
2928
import com.llamalad7.mixinextras.sugar.Local;
3029
import io.github.axolotlclient.modules.screenshotUtils.ScreenshotUtils;
3130
import net.minecraft.text.MutableText;
3231
import org.spongepowered.asm.mixin.Mixin;
3332
import org.spongepowered.asm.mixin.injection.At;
33+
import org.spongepowered.asm.mixin.injection.Redirect;
3434

3535
@Mixin(net.minecraft.client.util.ScreenshotUtils.class)
3636
public abstract class ScreenshotRecorderMixin {
3737

38-
@WrapOperation(method = "method_1661", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/TranslatableText;<init>(Ljava/lang/String;[Ljava/lang/Object;)V", ordinal = 0))
39-
private static MutableText axolotlclient$onScreenshotSaveSuccess(String s, Object[] objects, Operation<MutableText> original, @Local(argsOnly = true) File target) {
40-
return ScreenshotUtils.getInstance().onScreenshotTaken(original.call(s, objects), target);
38+
// for some reason @WrapOperation doesn't like injecting at <init>
39+
@Redirect(method = "method_1661", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", ordinal = 0))
40+
private static void axolotlclient$onScreenshotSaveSuccess(Consumer<MutableText> instance, Object t, @Local(argsOnly = true) File target) {
41+
instance.accept(ScreenshotUtils.getInstance().onScreenshotTaken((MutableText) t, target));
4142
}
4243
}

0 commit comments

Comments
 (0)