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

Commit 628ea5f

Browse files
committed
refactor skin loading
1 parent 990fa8a commit 628ea5f

File tree

19 files changed

+497
-622
lines changed

19 files changed

+497
-622
lines changed

1.16_combat-6/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinManagementScreen.java

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ private void promptForSkinDownload() {
258258
try {
259259
var bytes = t.skin().join();
260260
var out = ensureNonexistent(SKINS_DIR.resolve(t.skinKey()));
261-
Skin.Local.writeMetadata(out, Map.of(Skin.Local.CLASSIC_METADATA_KEY, t.classicModel(), "name", t.name(), "uuid", t.id(), "download_time", Instant.now()));
261+
Skin.LocalSkin.writeMetadata(out, Map.of(Skin.LocalSkin.CLASSIC_METADATA_KEY, t.classicModel(), "name", t.name(), "uuid", t.id(), "download_time", Instant.now()));
262262
Files.write(out, bytes);
263263
client.execute(this::loadSkinsList);
264264
Notifications.getInstance().addStatus("skins.notification.title", "skins.notification.import.online.downloaded", t.name());
@@ -348,23 +348,23 @@ private void loadSkinsList() {
348348
var profile = cachedProfile;
349349
int columns = Math.max(2, (width / 2 - 25) / LIST_SKIN_WIDTH);
350350
List<Skin> skins = new ArrayList<>(profile.skins());
351-
var hashes = skins.stream().map(Asset::textureKey).collect(Collectors.toSet());
352-
var defaultSkinHash = Auth.getInstance().getSkinManager().getDefaultSkinHash(account);
351+
var hashes = skins.stream().map(Asset::sha256).collect(Collectors.toSet());
352+
var defaultSkin = Skin.getDefaultSkin(account);
353353
var local = new ArrayList<>(loadLocalSkins());
354-
var localHashes = local.stream().collect(Collectors.toMap(Asset::textureKey, Function.identity(), (skin, skin2) -> skin));
354+
var localHashes = local.stream().collect(Collectors.toMap(Asset::sha256, Function.identity(), (skin, skin2) -> skin));
355355
local.removeIf(s -> !localHashes.containsValue(s));
356356
skins.replaceAll(s -> {
357357
if (s instanceof MSApi.MCProfile.OnlineSkin online) {
358-
if (localHashes.containsKey(s.textureKey()) && localHashes.get(s.textureKey()) instanceof Skin.Local file) {
359-
local.remove(localHashes.remove(s.textureKey()));
358+
if (localHashes.containsKey(s.sha256()) && localHashes.get(s.sha256()) instanceof Skin.LocalSkin file) {
359+
local.remove(localHashes.remove(s.sha256()));
360360
return new Skin.Shared(file, online);
361361
}
362362
}
363363
return s;
364364
});
365365
skins.addAll(local);
366-
if (!hashes.contains(defaultSkinHash)) {
367-
skins.add(null);
366+
if (!hashes.contains(defaultSkin.sha256())) {
367+
skins.add(defaultSkin);
368368
}
369369
populateSkinList(skins, columns);
370370
}
@@ -433,7 +433,7 @@ public void filesDragged(List<Path> packs) {
433433
var target = ensureNonexistent(SKINS_DIR.resolve(p.getFileName()));
434434
var skin = Auth.getInstance().getSkinManager().read(p, false);
435435
if (skin != null) {
436-
Files.write(target, skin.image().join());
436+
Files.write(target, skin.image());
437437
} else {
438438
AxolotlClientCommon.getInstance().getLogger().info("Skipping dragged file {} because it does not seem to be a valid skin!", p);
439439
Notifications.getInstance().addStatus("skins.notification.title", "skins.notification.not_copied", p.getFileName());
@@ -620,31 +620,33 @@ public Entry(int height, SkinWidget widget, @Nullable Text label) {
620620
}, skin.classicVariant() ? slimSprite : wideSprite));
621621
}
622622
if (asset != null) {
623-
if (asset.isLocal()) {
623+
if (asset instanceof Asset.Local local) {
624624
this.actionButtons.add(new SpriteButton(new TranslatableText("skins.manage.delete"), btn -> {
625625
btn.active = false;
626626
client.openScreen(new ConfirmScreen(confirmed -> {
627-
client.openScreen(SkinManagementScreen.this);
628627
if (confirmed) {
629628
try {
630-
Files.delete(asset.file());
631-
Skin.Local.deleteMetadata(asset.file());
632-
refreshCurrentList();
629+
Files.delete(local.file());
630+
Skin.LocalSkin.deleteMetadata(local.file());
633631
} catch (IOException e) {
634632
AxolotlClientCommon.getInstance().getLogger().warn("Failed to delete: ", e);
635633
}
636634
}
635+
client.openScreen(SkinManagementScreen.this);
637636
btn.active = true;
638637
}, new TranslatableText("skins.manage.delete.confirm"), (Text) (asset.active() ?
639638
new TranslatableText("skins.manage.delete.confirm.desc_active") :
640639
new TranslatableText("skins.manage.delete.confirm.desc")
641640
).br$color(Colors.RED.toInt())));
642641
}, new Identifier("axolotlclient", "textures/gui/sprites/delete.png")));
643642
}
644-
if (asset.supportsDownload() && !asset.isLocal()) {
643+
if (asset instanceof Asset.Online online && online.supportsDownload() && !(asset instanceof Asset.Local)) {
645644
this.actionButtons.add(new SpriteButton(new TranslatableText("skins.manage.download"), btn -> {
646645
btn.active = false;
647-
download(asset).thenRun(() -> btn.active = true);
646+
download(asset).thenRun(() -> {
647+
refreshCurrentList();
648+
btn.active = true;
649+
});
648650
}, new Identifier("axolotlclient", "textures/gui/sprites/download.png")));
649651
}
650652
}
@@ -668,13 +670,17 @@ public void renderButton(MatrixStack guiGraphics, int mouseX, int mouseY, float
668670
btn.active = false;
669671
Consumer<CompletableFuture<MSApi.MCProfile>> consumer = f -> f.thenAcceptAsync(p -> {
670672
cachedProfile = p;
671-
refreshCurrentList();
673+
if (client.currentScreen == SkinManagementScreen.this) {
674+
refreshCurrentList();
675+
} else {
676+
client.openScreen(SkinManagementScreen.this);
677+
}
672678
}).exceptionally(t -> {
673679
AxolotlClientCommon.getInstance().getLogger().warn("Failed to equip asset!", t);
674680
equipping = false;
675681
return null;
676682
});
677-
if (asset instanceof Skin && !current.getSkin().isLocal()) {
683+
if (asset instanceof Skin && !(current.getSkin() instanceof Skin.Local)) {
678684
client.openScreen(new ConfirmScreen(confirmed -> {
679685
if (confirmed) {
680686
consumer.accept(download(current.getSkin()).thenCompose(a -> widget.equip()));
@@ -691,18 +697,17 @@ public void renderButton(MatrixStack guiGraphics, int mouseX, int mouseY, float
691697
}
692698

693699
private @NotNull CompletableFuture<?> download(Asset asset) {
694-
return asset.image().thenAcceptAsync(b -> {
700+
return CompletableFuture.runAsync(() -> {
695701
try {
696-
var out = SKINS_DIR.resolve(asset.textureKey());
702+
var out = SKINS_DIR.resolve(asset.sha256());
697703
Files.createDirectories(out.getParent());
698-
Files.write(out, b);
704+
Files.write(out, asset.image());
699705
if (asset instanceof Skin skin) {
700-
Skin.Local.writeMetadata(out, Map.of(Skin.Local.CLASSIC_METADATA_KEY, skin.classicVariant()));
706+
Skin.LocalSkin.writeMetadata(out, Map.of(Skin.LocalSkin.CLASSIC_METADATA_KEY, skin.classicVariant()));
701707
}
702708
} catch (IOException e) {
703709
AxolotlClientCommon.getInstance().getLogger().warn("Failed to download: ", e);
704710
}
705-
refreshCurrentList();
706711
});
707712
}
708713

1.16_combat-6/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinManager.java

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,16 @@
2929
import java.nio.file.Path;
3030
import java.util.Comparator;
3131
import java.util.Set;
32-
import java.util.concurrent.CompletableFuture;
3332
import java.util.concurrent.ConcurrentSkipListSet;
3433

3534
import com.google.common.hash.Hashing;
3635
import io.github.axolotlclient.AxolotlClientCommon;
37-
import io.github.axolotlclient.api.util.UUIDHelper;
38-
import io.github.axolotlclient.bridge.AxoMinecraftClient;
3936
import io.github.axolotlclient.bridge.util.AxoIdentifier;
4037
import io.github.axolotlclient.mixin.skins.PlayerSkinTextureAccessor;
41-
import io.github.axolotlclient.modules.auth.Account;
4238
import io.github.axolotlclient.util.ClientColors;
4339
import net.minecraft.client.MinecraftClient;
4440
import net.minecraft.client.texture.NativeImage;
4541
import net.minecraft.client.texture.NativeImageBackedTexture;
46-
import net.minecraft.client.util.DefaultSkinHelper;
4742
import net.minecraft.util.Identifier;
4843

4944
public class SkinManager {
@@ -78,85 +73,54 @@ public Skin read(Path p, boolean fix) {
7873
} else {
7974
slim = ClientColors.ARGB.alpha(img.getPixelColor(50, 16)) == 0;
8075
}
81-
var metadata = Skin.Local.readMetadata(p);
82-
if (metadata != null && metadata.containsKey(Skin.Local.CLASSIC_METADATA_KEY)) {
83-
slim = !(boolean) metadata.get(Skin.Local.CLASSIC_METADATA_KEY);
76+
var metadata = Skin.LocalSkin.readMetadata(p);
77+
if (metadata != null && metadata.containsKey(Skin.LocalSkin.CLASSIC_METADATA_KEY)) {
78+
slim = !(boolean) metadata.get(Skin.LocalSkin.CLASSIC_METADATA_KEY);
8479
}
8580
}
8681
}
87-
return new Skin.Local(!slim, p, sha256);
82+
return new Skin.LocalSkin(!slim, p, in, sha256);
8883
} catch (Exception e) {
8984
AxolotlClientCommon.getInstance().getLogger().warn("Failed to probe skin: ", e);
9085
}
9186
return null;
9287
}
9388

94-
public CompletableFuture<AxoIdentifier> loadSkin(Skin skin) {
95-
var rl = AxoIdentifier.of(AxolotlClientCommon.MODID, "skins/" + skin.textureKey());
89+
public AxoIdentifier loadSkin(Skin skin) {
90+
var rl = AxoIdentifier.of(AxolotlClientCommon.MODID, "skins/" + skin.sha256());
9691
if (loadedTextures.contains(rl)) {
97-
return CompletableFuture.completedFuture(rl);
92+
return rl;
9893
}
9994

100-
return skin.image().thenApplyAsync(bytes -> {
101-
try (var stream = new ByteArrayInputStream(bytes)) {
102-
var tex = new NativeImageBackedTexture(NativeImage.read(stream));
103-
tex.upload();
104-
MinecraftClient.getInstance().getTextureManager().registerTexture((Identifier) rl, tex);
105-
} catch (IOException e) {
106-
throw new UncheckedIOException(e);
107-
}
108-
loadedTextures.add(rl);
109-
return rl;
110-
}, AxoMinecraftClient.getInstance()).handle((v, t) -> {
111-
if (t != null) {
112-
AxolotlClientCommon.getInstance().getLogger().warn("Failed to load skin!", t);
113-
}
114-
return v;
115-
});
95+
try (var stream = new ByteArrayInputStream(skin.image())) {
96+
var tex = new NativeImageBackedTexture(NativeImage.read(stream));
97+
tex.upload();
98+
MinecraftClient.getInstance().getTextureManager().registerTexture((Identifier) rl, tex);
99+
} catch (IOException e) {
100+
throw new UncheckedIOException(e);
101+
}
102+
loadedTextures.add(rl);
103+
return rl;
116104
}
117105

118106
public AxoIdentifier loadCape(Cape cape) {
119-
var rl = AxoIdentifier.of(AxolotlClientCommon.MODID, "capes/" + cape.textureKey());
107+
var rl = AxoIdentifier.of(AxolotlClientCommon.MODID, "capes/" + cape.id());
120108
if (loadedTextures.contains(rl)) {
121109
return rl;
122110
}
123111

124-
return cape.image().thenApplyAsync(bytes -> {
125-
try (var stream = new ByteArrayInputStream(bytes)) {
126-
var tex = new NativeImageBackedTexture(NativeImage.read(stream));
127-
MinecraftClient.getInstance().getTextureManager().registerTexture((Identifier) rl, tex);
128-
} catch (IOException e) {
129-
throw new UncheckedIOException(e);
130-
}
131-
loadedTextures.add(rl);
132-
return rl;
133-
}, AxoMinecraftClient.getInstance()).handle((id, t) -> {
134-
if (t != null) {
135-
AxolotlClientCommon.getInstance().getLogger().warn("Failed to load cape!", t);
136-
}
137-
return id;
138-
}).getNow(null);
139-
112+
try (var stream = new ByteArrayInputStream(cape.image())) {
113+
var tex = new NativeImageBackedTexture(NativeImage.read(stream));
114+
MinecraftClient.getInstance().getTextureManager().registerTexture((Identifier) rl, tex);
115+
} catch (IOException e) {
116+
throw new UncheckedIOException(e);
117+
}
118+
loadedTextures.add(rl);
119+
return rl;
140120
}
141121

142122
public void releaseAll() {
143123
loadedTextures.forEach(id -> MinecraftClient.getInstance().getTextureManager().destroyTexture((Identifier) id));
144124
loadedTextures.clear();
145125
}
146-
147-
@SuppressWarnings("UnstableApiUsage")
148-
public String getDefaultSkinHash(Account account) {
149-
var skin = DefaultSkinHelper.getTexture(UUIDHelper.fromUndashed(account.getUuid()));
150-
var mc = MinecraftClient.getInstance();
151-
var resourceManager = mc.getResourceManager();
152-
try {
153-
var res = resourceManager.getResource(skin);
154-
try (
155-
var in = res.br$asStream()) {
156-
return Hashing.sha256().hashBytes(in.readAllBytes()).toString();
157-
}
158-
} catch (IOException ignored) {
159-
}
160-
return null;
161-
}
162126
}

1.16_combat-6/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinWidget.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import java.util.concurrent.CompletableFuture;
2626

27-
import io.github.axolotlclient.api.util.UUIDHelper;
2827
import io.github.axolotlclient.bridge.util.AxoIdentifier;
2928
import io.github.axolotlclient.modules.auth.Account;
3029
import io.github.axolotlclient.modules.auth.Auth;
@@ -33,7 +32,6 @@
3332
import lombok.Setter;
3433
import net.minecraft.client.gui.widget.AbstractButtonWidget;
3534
import net.minecraft.client.sound.SoundManager;
36-
import net.minecraft.client.util.DefaultSkinHelper;
3735
import net.minecraft.client.util.math.MatrixStack;
3836
import net.minecraft.text.LiteralText;
3937
import net.minecraft.text.MutableText;
@@ -81,18 +79,9 @@ public void renderButton(MatrixStack guiGraphics, int mouseX, int mouseY, float
8179
float scale = FIT_SCALE * this.getHeight() / MODEL_HEIGHT;
8280
float pivotY = -1.0625F;
8381

84-
AxoIdentifier skinRl;
85-
boolean classic;
8682
SkinManager skinManager = Auth.getInstance().getSkinManager();
87-
CompletableFuture<AxoIdentifier> loader = skin == null ? null : skinManager.loadSkin(skin);
88-
if (loader != null && loader.isDone()) {
89-
skinRl = loader.join();
90-
classic = skin.classicVariant();
91-
} else {
92-
var uuid = UUIDHelper.fromUndashed(owner.getUuid());
93-
classic = DefaultSkinHelper.getModel(uuid).equals("default");
94-
skinRl = DefaultSkinHelper.getTexture(uuid);
95-
}
83+
AxoIdentifier skinRl = skinManager.loadSkin(skin);
84+
boolean classic = skin.classicVariant();
9685
var capeRl = cape == null ? null : skinManager.loadCape(cape);
9786

9887
SkinRenderer.render(guiGraphics, classic, (Identifier) skinRl, (Identifier) capeRl, this.rotationX, this.rotationY, pivotY, this.getX(), this.getY(), this.getX() + getWidth(), this.getY() + getHeight(), scale);

0 commit comments

Comments
 (0)