From 82fc1c067dc595f689f61c8ee992ade7aafcca73 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 13 Feb 2024 10:08:26 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20#2153:=20=E6=AD=A3=E7=A1=AE=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=A6=BB=E7=BA=BF=E8=B4=A6=E6=88=B7=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=20(#2788)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update * update * update * update * fix checkstyle * update --- .../jackhuang/hmcl/game/TexturesLoader.java | 100 ++++++++++++------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/TexturesLoader.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/TexturesLoader.java index 7c472cc82..3e40976cb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/TexturesLoader.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/TexturesLoader.java @@ -17,7 +17,10 @@ */ package org.jackhuang.hmcl.game; +import javafx.beans.InvalidationListener; +import javafx.beans.WeakInvalidationListener; import javafx.beans.binding.ObjectBinding; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.canvas.Canvas; @@ -29,9 +32,13 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.ServerResponseMalformedException; import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccount; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.offline.Skin; import org.jackhuang.hmcl.auth.yggdrasil.*; import org.jackhuang.hmcl.task.FileDownloadTask; -import org.jackhuang.hmcl.util.ResourceNotFoundError; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.javafx.BindingMapping; @@ -141,16 +148,7 @@ public final class TexturesLoader { } private static void loadDefaultSkin(String path, TextureModel model) { - Image skin; - try { - skin = new Image(path); - if (skin.isError()) - throw skin.getException(); - } catch (Throwable e) { - throw new ResourceNotFoundError("Cannot load default skin from " + path, e); - } - - DEFAULT_SKINS.put(model, new LoadedTexture(skin, singletonMap("model", model.modelName))); + DEFAULT_SKINS.put(model, new LoadedTexture(FXUtils.newBuiltinImage(path), singletonMap("model", model.modelName))); } public static LoadedTexture getDefaultSkin(TextureModel model) { @@ -188,27 +186,61 @@ public final class TexturesLoader { }, uuidFallback); } - public static ObjectBinding skinBinding(Account account) { + public static ObservableValue skinBinding(Account account) { LoadedTexture uuidFallback = getDefaultSkin(TextureModel.detectUUID(account.getUUID())); - return BindingMapping.of(account.getTextures()) - .map(textures -> textures - .flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN))) - .filter(it -> StringUtils.isNotBlank(it.getUrl()))) - .asyncMap(it -> { - if (it.isPresent()) { - Texture texture = it.get(); - return CompletableFuture.supplyAsync(() -> { - try { - return loadTexture(texture); - } catch (Throwable e) { - LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e); - return uuidFallback; + if (account instanceof OfflineAccount) { + OfflineAccount offlineAccount = (OfflineAccount) account; + + SimpleObjectProperty binding = new SimpleObjectProperty<>(); + InvalidationListener listener = o -> { + Skin skin = offlineAccount.getSkin(); + String username = offlineAccount.getUsername(); + + binding.set(uuidFallback); + if (skin != null) { + skin.load(username).setExecutor(POOL).whenComplete(Schedulers.javafx(), (result, exception) -> { + if (exception != null) { + LOG.log(Level.WARNING, "Failed to load texture", exception); + } else if (result != null && result.getSkin() != null && result.getSkin().getImage() != null) { + Map metadata; + if (result.getModel() != null) { + metadata = singletonMap("model", result.getModel().modelName); + } else { + metadata = emptyMap(); } - }, POOL); - } else { + + binding.set(new LoadedTexture(result.getSkin().getImage(), metadata)); + } + }).start(); + } + }; + + listener.invalidated(offlineAccount); + + binding.addListener(new Holder<>(listener)); + offlineAccount.addListener(new WeakInvalidationListener(listener)); + + return binding; + } else { + return BindingMapping.of(account.getTextures()) + .asyncMap(textures -> { + if (textures.isPresent()) { + Texture texture = textures.get().get(TextureType.SKIN); + if (texture != null && StringUtils.isNotBlank(texture.getUrl())) { + return CompletableFuture.supplyAsync(() -> { + try { + return loadTexture(texture); + } catch (Throwable e) { + LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e); + return uuidFallback; + } + }, POOL); + } + } + return CompletableFuture.completedFuture(uuidFallback); - } - }, uuidFallback); + }, uuidFallback); + } } // ==== @@ -277,9 +309,9 @@ public final class TexturesLoader { static final WeakHashMap hole = new WeakHashMap<>(); final WeakReference canvasRef; - final ObjectBinding binding; + final ObservableValue binding; - SkinBindingChangeListener(Canvas canvas, ObjectBinding binding) { + SkinBindingChangeListener(Canvas canvas, ObservableValue binding) { this.canvasRef = new WeakReference<>(canvas); this.binding = binding; } @@ -293,14 +325,14 @@ public final class TexturesLoader { } } - public static void fxAvatarBinding(Canvas canvas, ObjectBinding skinBinding) { + public static void fxAvatarBinding(Canvas canvas, ObservableValue skinBinding) { synchronized (SkinBindingChangeListener.hole) { SkinBindingChangeListener oldListener = SkinBindingChangeListener.hole.remove(canvas); if (oldListener != null) oldListener.binding.removeListener(oldListener); SkinBindingChangeListener listener = new SkinBindingChangeListener(canvas, skinBinding); - listener.changed(skinBinding, null, skinBinding.get()); + listener.changed(skinBinding, null, skinBinding.getValue()); skinBinding.addListener(listener); SkinBindingChangeListener.hole.put(canvas, listener); @@ -312,7 +344,7 @@ public final class TexturesLoader { } public static void bindAvatar(Canvas canvas, Account account) { - if (account instanceof YggdrasilAccount || account instanceof MicrosoftAccount) + if (account instanceof YggdrasilAccount || account instanceof MicrosoftAccount || account instanceof OfflineAccount) fxAvatarBinding(canvas, skinBinding(account)); else { unbindAvatar(canvas);