src/main/java/wtf/sten/vault/ui/VaultScreen.java
package wtf.sten.vault.ui;
import com.google.gson.JsonObject;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Util;
import wtf.sten.vault.Faces;
import wtf.sten.vault.Proxy;
import wtf.sten.vault.SessionSwap;
import wtf.sten.vault.StenNet;
import wtf.sten.vault.StenVault;
import wtf.sten.vault.VaultConfig;
import java.net.URI;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class VaultScreen extends Screen {
private static final String[] RANKS = {"", "VIP", "VIP+", "MVP", "MVP+", "MVP++", "NONE"};
private final Screen parent;
private EditBox search;
private EditBox capeBox;
private EditBox minValueBox;
private EditBox minDonutBox;
private EditBox proxyBox;
private int scroll;
private String lastQuery = "";
private int lastCount = -1;
private String lastPairing = "";
private String lastSort = "";
private String lastRank = "";
private String lastCape = "";
private String lastMinValue = "";
private String lastMinDonut = "";
private final List<GlassButton> rows = new ArrayList<>();
private final List<JsonObject> visible = new ArrayList<>();
public VaultScreen(Screen parent) {
super(Component.literal("STEN Vault"));
this.parent = parent;
}
@Override
protected void init() {
super.init();
clearWidgets();
rows.clear();
int cx = this.width / 2;
search = field(24, 52, this.width - 48, "Filter name, rank, uid, cape");
capeBox = field(24, 76, 110, "Cape");
minValueBox = field(140, 76, 90, "Min $");
minDonutBox = field(236, 76, 90, "Min Donut");
proxyBox = field(24, this.height - 52, 220, "socks5://user:pass@host:port");
capeBox.setValue(VaultConfig.get().capeFilter);
if (VaultConfig.get().minValue > 0) minValueBox.setValue(trimNum(VaultConfig.get().minValue));
if (VaultConfig.get().minDonut > 0) minDonutBox.setValue(trimNum(VaultConfig.get().minDonut));
proxyBox.setValue(VaultConfig.get().proxy);
if (StenNet.session.signedIn()) {
addRenderableWidget(new GlassButton(this.width - 330, 12, 74, 22, Component.literal("Refresh"), false, b -> {
StenNet.status = "Loading sten.wtf…";
CompletableFuture.runAsync(StenNet::refreshAccounts);
}));
addRenderableWidget(new GlassButton(this.width - 248, 12, 74, 22, Component.literal(sortLabel()), false, b -> {
cycleSort();
relayout();
}));
addRenderableWidget(new GlassButton(this.width - 166, 12, 74, 22, Component.literal(rankLabel()), false, b -> {
cycleRank();
relayout();
}));
addRenderableWidget(new GlassButton(this.width - 84, 12, 72, 22, Component.literal("Sign out"), false, b -> {
StenNet.signOut();
relayout();
}));
} else {
addRenderableWidget(new GlassButton(cx - 110, 12, 220, 24, Component.literal("Sign in with sten.wtf"), true, b -> {
StenNet.status = "Opening sten.wtf…";
StenNet.beginLogin(msg -> StenNet.status = msg);
}));
}
addRenderableWidget(new GlassButton(252, this.height - 52, 70, 20, Component.literal("Proxy"), true, b -> {
String next = proxyBox.getValue().trim();
if (!next.isEmpty() && Proxy.parse(next) == null) {
StenNet.status = "Invalid SOCKS5 proxy";
return;
}
VaultConfig.get().proxy = next;
VaultConfig.get().save();
StenNet.status = next.isEmpty() ? "Direct connection" : "Proxy set";
}));
addRenderableWidget(new GlassButton(24, this.height - 28, 82, 20, Component.literal("Dashboard"), true, b -> Util.getPlatform().openUri(URI.create(StenVault.DASHBOARD))));
addRenderableWidget(new GlassButton(110, this.height - 28, 70, 20, Component.literal("Source"), false, b -> Util.getPlatform().openUri(URI.create(StenVault.SOURCE))));
addRenderableWidget(new GlassButton(184, this.height - 28, 62, 20, Component.literal("Bugs"), false, b -> Util.getPlatform().openUri(URI.create(StenVault.ISSUES))));
addRenderableWidget(new GlassButton(250, this.height - 28, 82, 20, Component.literal("Download"), false, b -> Util.getPlatform().openUri(URI.create(StenVault.DOWNLOAD))));
rebuildRows();
}
private EditBox field(int x, int y, int w, String hint) {
EditBox box = new EditBox(this.font, x, y, w, 18, Component.literal(hint));
box.setHint(Component.literal(hint));
box.setBordered(false);
box.setTextColor(Glass.INK);
addRenderableWidget(box);
return box;
}
private void relayout() {
this.init();
}
private void cycleSort() {
VaultConfig cfg = VaultConfig.get();
cfg.sort = switch (cfg.sort) {
case "newest" -> "value";
case "value" -> "donut";
case "donut" -> "networth";
case "networth" -> "level";
case "level" -> "capes";
case "capes" -> "name";
default -> "newest";
};
cfg.save();
}
private void cycleRank() {
VaultConfig cfg = VaultConfig.get();
String current = cfg.rankFilter == null ? "" : cfg.rankFilter;
int idx = 0;
for (int i = 0; i < RANKS.length; i++) {
if (RANKS[i].equalsIgnoreCase(current)) {
idx = (i + 1) % RANKS.length;
break;
}
}
cfg.rankFilter = RANKS[idx];
cfg.save();
}
private String sortLabel() {
return switch (VaultConfig.get().sort) {
case "value" -> "Value";
case "donut" -> "Donut";
case "networth" -> "NW";
case "level" -> "Level";
case "capes" -> "Capes";
case "name" -> "Name";
default -> "New";
};
}
private String rankLabel() {
String rank = VaultConfig.get().rankFilter;
return rank == null || rank.isBlank() ? "Rank" : rank;
}
private void persistFilters() {
VaultConfig cfg = VaultConfig.get();
cfg.capeFilter = capeBox == null ? "" : capeBox.getValue().trim();
cfg.minValue = parseNum(minValueBox);
cfg.minDonut = parseNum(minDonutBox);
cfg.save();
}
private void rebuildRows() {
persistFilters();
for (GlassButton row : rows) {
removeWidget(row);
}
rows.clear();
visible.clear();
visible.addAll(filtered());
int y = 104;
int visibleCount = Math.max(1, (this.height - 168) / 44);
int start = Math.min(Math.max(scroll, 0), Math.max(0, visible.size() - visibleCount));
scroll = start;
int end = Math.min(visible.size(), start + visibleCount);
for (int i = start; i < end; i++) {
JsonObject account = visible.get(i);
String uid = str(account, "uid");
String name = str(account, "username");
if (name.isEmpty()) name = uid;
int rowY = y;
String selectedName = name;
GlassButton btn = new GlassButton(64, rowY, this.width - 88, 40, Component.literal(""), false, b -> switchNow(uid, selectedName));
rows.add(btn);
addRenderableWidget(btn);
y += 44;
}
lastCount = StenNet.accounts.size();
lastQuery = query();
lastPairing = StenNet.pairingCode;
lastSort = VaultConfig.get().sort;
lastRank = VaultConfig.get().rankFilter;
lastCape = capeBox == null ? "" : capeBox.getValue();
lastMinValue = minValueBox == null ? "" : minValueBox.getValue();
lastMinDonut = minDonutBox == null ? "" : minDonutBox.getValue();
}
private void switchNow(String uid, String name) {
StenNet.status = "Switching on sten.wtf…";
StenNet.session.selectedUid = uid;
StenNet.session.selectedName = name == null ? "" : name;
StenNet.session.save();
CompletableFuture.runAsync(() -> {
try {
JsonObject minted = StenNet.switchAccount(uid);
minecraft.execute(() -> {
try {
SessionSwap.apply(minted);
StenNet.status = "Now playing as " + (name == null || name.isEmpty() ? "vault account" : name);
} catch (Exception ex) {
StenNet.status = ex.getMessage();
}
});
} catch (Exception ex) {
StenNet.status = ex.getMessage() == null ? "Switch failed" : ex.getMessage();
}
});
}
private List<JsonObject> filtered() {
String q = query();
VaultConfig cfg = VaultConfig.get();
String rankWant = cfg.rankFilter == null ? "" : cfg.rankFilter.toLowerCase(Locale.ROOT);
String capeWant = cfg.capeFilter == null ? "" : cfg.capeFilter.toLowerCase(Locale.ROOT);
List<JsonObject> out = new ArrayList<>();
for (JsonObject account : StenNet.accounts) {
String rank = rankOf(account).toLowerCase(Locale.ROOT);
String cape = str(account, "capes").toLowerCase(Locale.ROOT);
String blob = (str(account, "username") + " " + str(account, "uid") + " " + rank + " " + cape).toLowerCase(Locale.ROOT);
if (!q.isEmpty() && !blob.contains(q)) continue;
if (!rankWant.isEmpty() && !rank.equals(rankWant) && !rank.contains(rankWant)) continue;
if (!capeWant.isEmpty() && !cape.contains(capeWant)) continue;
if (cfg.minValue > 0 && valueOf(account) < cfg.minValue) continue;
if (cfg.minDonut > 0 && donutNum(account) < cfg.minDonut) continue;
out.add(account);
}
Comparator<JsonObject> cmp = switch (cfg.sort) {
case "value" -> Comparator.comparingDouble(VaultScreen::valueOf).reversed();
case "donut" -> Comparator.comparingDouble(VaultScreen::donutNum).reversed();
case "networth" -> Comparator.comparingDouble(VaultScreen::networthOf).reversed();
case "level" -> Comparator.comparingDouble(VaultScreen::levelOf).reversed();
case "capes" -> Comparator.comparingInt(VaultScreen::capeCount).reversed();
case "name" -> Comparator.comparing(a -> str(a, "username").toLowerCase(Locale.ROOT));
default -> (a, b) -> 0;
};
out.sort(cmp);
return out;
}
private String query() {
return search == null ? "" : search.getValue().trim().toLowerCase(Locale.ROOT);
}
@Override
public void tick() {
super.tick();
if (lastCount != StenNet.accounts.size() || !query().equals(lastQuery) || !StenNet.pairingCode.equals(lastPairing)
|| !VaultConfig.get().sort.equals(lastSort) || !VaultConfig.get().rankFilter.equals(lastRank)
|| (capeBox != null && !capeBox.getValue().equals(lastCape))
|| (minValueBox != null && !minValueBox.getValue().equals(lastMinValue))
|| (minDonutBox != null && !minDonutBox.getValue().equals(lastMinDonut))) {
rebuildRows();
}
}
@Override
public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTick) {
Glass.atmosphere(graphics, this.width, this.height);
Glass.panel(graphics, 16, 8, this.width - 32, 40, Glass.PANEL_STRONG);
Glass.wordmark(graphics, this.font, 28, 20);
graphics.text(this.font, Component.literal("2.1.2"), 28 + this.font.width("sten.wtf") + 8, 20, Glass.CYAN, false);
if (StenNet.session.signedIn()) {
String who = StenNet.session.name.isEmpty() ? StenNet.session.userId : StenNet.session.name;
String playing = StenNet.session.selectedName == null || StenNet.session.selectedName.isBlank()
? who : (who + " · " + StenNet.session.selectedName);
graphics.text(this.font, Component.literal(playing), 28, 34, Glass.INK_SOFT, false);
}
Glass.panel(graphics, 20, 48, this.width - 40, 26, Glass.PANEL);
Glass.panel(graphics, 20, 72, 110, 26, Glass.PANEL);
Glass.panel(graphics, 136, 72, 90, 26, Glass.PANEL);
Glass.panel(graphics, 232, 72, 90, 26, Glass.PANEL);
Glass.panel(graphics, 20, this.height - 56, 220, 26, Glass.PANEL);
super.extractRenderState(graphics, mouseX, mouseY, partialTick);
int y = 104;
int shown = Math.max(1, (this.height - 168) / 44);
int start = Math.min(scroll, Math.max(0, visible.size() - shown));
int end = Math.min(visible.size(), start + shown);
for (int i = start; i < end; i++) {
JsonObject account = visible.get(i);
UUID uuid = SessionSwap.parseUuid(str(account, "uuid"));
String name = str(account, "username");
if (name.isEmpty()) name = str(account, "uid");
Faces.draw(graphics, this.minecraft, uuid, name, 28, y + 4, 32);
graphics.text(this.font, Component.literal(name), 72, y + 8, Glass.INK, false);
String rank = rankOf(account);
String stats = rank.isEmpty() ? money(valueOf(account)) : (rank + " · " + money(valueOf(account)));
double nw = networthOf(account);
double donut = donutNum(account);
double level = levelOf(account);
if (level > 1) stats += " · L" + (int) level;
if (nw > 0) stats += " · NW " + money(nw);
if (donut > 0) stats += " · D " + money(donut);
int capes = capeCount(account);
if (capes > 0) stats += " · " + capes + " capes";
graphics.text(this.font, Component.literal(stats), 72, y + 24, Glass.INK_SOFT, false);
y += 44;
}
if (!StenNet.pairingCode.isEmpty()) {
int pw = 300;
int ph = 118;
int px = (this.width - pw) / 2;
int py = (this.height - ph) / 2;
Glass.panel(graphics, px, py, pw, ph, Glass.PANEL_STRONG);
graphics.centeredText(this.font, StenNet.officialBuild ? "Official sten.wtf build" : "Type this code on sten.wtf", this.width / 2, py + 18, Glass.INK_SOFT);
graphics.centeredText(this.font, StenNet.pairingCode, this.width / 2, py + 52, Glass.CYAN);
graphics.centeredText(this.font, "Keep Minecraft open", this.width / 2, py + 86, Glass.INK_SOFT);
} else if (!StenNet.session.signedIn()) {
graphics.centeredText(this.font, "Sign in with sten.wtf to load your vault.", this.width / 2, this.height / 2, Glass.INK_SOFT);
} else if (StenNet.accounts.isEmpty()) {
graphics.centeredText(this.font, StenNet.status.isEmpty() ? "No secured accounts in this vault." : StenNet.status, this.width / 2, this.height / 2, Glass.INK_SOFT);
} else if (visible.isEmpty()) {
graphics.centeredText(this.font, "No accounts match these filters.", this.width / 2, this.height / 2, Glass.INK_SOFT);
}
String footer = StenNet.status;
if (StenNet.session.signedIn() && !StenNet.accounts.isEmpty()) {
footer = visible.size() + " / " + StenNet.accounts.size() + (footer.isEmpty() ? "" : " · " + footer);
}
if (!footer.isEmpty() && StenNet.pairingCode.isEmpty()) {
graphics.centeredText(this.font, footer, this.width / 2, this.height - 72, Glass.CYAN);
}
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
if (scrollY > 0) scroll = Math.max(0, scroll - 1);
if (scrollY < 0) scroll++;
rebuildRows();
return true;
}
@Override
public void onClose() {
persistFilters();
this.minecraft.setScreenAndShow(parent);
}
@Override
public boolean isPauseScreen() {
return false;
}
private static String rankOf(JsonObject account) {
String rank = str(account, "rank");
if (!rank.isEmpty()) return rank;
try {
if (account.has("stats") && account.get("stats").isJsonObject()) {
JsonObject stats = account.getAsJsonObject("stats");
if (stats.has("hypixel") && stats.get("hypixel").isJsonObject()) {
return str(stats.getAsJsonObject("hypixel"), "rank");
}
}
} catch (Exception ignored) {
}
return "";
}
private static double valueOf(JsonObject account) {
try {
if (account.has("stats") && account.get("stats").isJsonObject()) {
JsonObject stats = account.getAsJsonObject("stats");
if (stats.has("totalAccountValue")) return stats.get("totalAccountValue").getAsDouble();
if (stats.has("hypixel") && stats.get("hypixel").isJsonObject()) {
JsonObject hypixel = stats.getAsJsonObject("hypixel");
if (hypixel.has("value") && hypixel.get("value").isJsonObject()) {
JsonObject value = hypixel.getAsJsonObject("value");
if (value.has("totalValue")) return value.get("totalValue").getAsDouble();
}
if (hypixel.has("value") && hypixel.get("value").isJsonPrimitive()) {
return hypixel.get("value").getAsDouble();
}
}
}
} catch (Exception ignored) {
}
return 0;
}
private static double networthOf(JsonObject account) {
try {
JsonObject sky = account.getAsJsonObject("stats").getAsJsonObject("hypixel").getAsJsonObject("skyblock");
if (sky != null && sky.has("networth")) return sky.get("networth").getAsDouble();
} catch (Exception ignored) {
}
return 0;
}
private static double levelOf(JsonObject account) {
try {
JsonObject stats = account.getAsJsonObject("stats");
if (stats.has("LVL")) return stats.get("LVL").getAsDouble();
JsonObject hypixel = stats.getAsJsonObject("hypixel");
if (hypixel != null && hypixel.has("networkLevel")) return hypixel.get("networkLevel").getAsDouble();
} catch (Exception ignored) {
}
return 0;
}
private static int capeCount(JsonObject account) {
String capes = str(account, "capes");
if (capes.isEmpty() || capes.equalsIgnoreCase("None")) return 0;
int n = 1;
for (int i = 0; i < capes.length(); i++) {
if (capes.charAt(i) == ',') n++;
}
return n;
}
private static double donutNum(JsonObject account) {
try {
if (account.has("stats") && account.get("stats").isJsonObject()) {
JsonObject donut = account.getAsJsonObject("stats").getAsJsonObject("donut");
if (donut == null) return 0;
if (donut.has("money")) {
try {
return donut.get("money").getAsDouble();
} catch (Exception ignored) {
String raw = donut.get("money").getAsString().replace("$", "").replace(",", "");
return Double.parseDouble(raw);
}
}
}
} catch (Exception ignored) {
}
return 0;
}
private static String money(double n) {
if (n >= 1_000_000_000) return String.format(Locale.US, "$%.2fB", n / 1_000_000_000);
if (n >= 1_000_000) return String.format(Locale.US, "$%.2fM", n / 1_000_000);
if (n >= 1000) return String.format(Locale.US, "$%.1fk", n / 1000);
if (n <= 0) return "$0";
return String.format(Locale.US, "$%.0f", n);
}
private static double parseNum(EditBox box) {
if (box == null) return 0;
try {
String raw = box.getValue().trim().replace("$", "").replace(",", "");
if (raw.isEmpty()) return 0;
return Math.max(0, Double.parseDouble(raw));
} catch (Exception ignored) {
return 0;
}
}
private static String trimNum(double n) {
if (n == (long) n) return Long.toString((long) n);
return String.format(Locale.US, "%.2f", n);
}
private static String str(JsonObject obj, String key) {
if (obj == null || !obj.has(key) || obj.get(key).isJsonNull()) return "";
try {
return obj.get(key).getAsString();
} catch (Exception ignored) {
return "";
}
}
}