Skip to content
This repository was archived by the owner on Jan 3, 2020. It is now read-only.

Commit 57a4113

Browse files
committed
asynchronous skull texture loading
1 parent 4693cde commit 57a4113

File tree

5 files changed

+177
-10
lines changed

5 files changed

+177
-10
lines changed

src/main/java/org/devinprogress/uniskinmod/UniSkinCore.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
import java.lang.reflect.Field;
2424
import java.net.HttpURLConnection;
2525
import java.net.URL;
26+
import java.util.Collections;
27+
import java.util.Set;
2628
import java.util.UUID;
29+
import java.util.concurrent.ConcurrentHashMap;
2730
import java.util.concurrent.ExecutionException;
2831

2932
/**
@@ -284,21 +287,36 @@ public ResourceLocation getDynamicSkinResource(NetworkPlayerInfo player) {
284287
/**
285288
* called from TileEntitySkllRenderer.renderSkull()
286289
*/
290+
private final Set<String> loading = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
291+
287292
public ResourceLocation getDynamicSkinResourceForSkull(GameProfile gp, ResourceLocation def) {
288293
if (gp == null) return def;
289-
String name = gp.getName();
294+
final String name = gp.getName();
290295
if (name == null || name.length() <= 0) return def;
291-
try {
292-
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.get(name);
293-
if (s.skin != null && s.skin.length != 0) {
294-
double spf = (double) s.skinInterval / (double) s.skin.length;
295-
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.skinInterval) / spf)) % (s.skin.length);
296-
return s.skin[id];
297-
}
298-
} catch (ExecutionException ex) {
299-
UniSkinMod.log.catching(Level.WARN, ex);
296+
297+
if (loading.contains(name)) return def;
298+
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.getIfPresent(name);
299+
if (s == null) {
300+
loading.add(name);
301+
new Thread(new Runnable() {
302+
@Override
303+
public void run() {
304+
try {
305+
dynamicSkinManager.cache.get(name);
306+
} catch (ExecutionException ex) {
307+
UniSkinMod.log.catching(Level.WARN, ex);
308+
}
309+
loading.remove(name);
310+
}
311+
}, "Skull-Texture-Fetch-" + name).start();
300312
return def;
301313
}
314+
if (s.skin != null && s.skin.length != 0) {
315+
double spf = (double) s.skinInterval / (double) s.skin.length;
316+
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.skinInterval) / spf)) % (s.skin.length);
317+
return s.skin[id];
318+
}
319+
302320
return def;
303321
}
304322

src/main/java/org/devinprogress/uniskinmod/UniSkinMod.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
import org.apache.logging.log4j.Level;
1414
import org.apache.logging.log4j.Logger;
1515
import org.devinprogress.uniskinmod.coremod.IGetTexture_old;
16+
import org.devinprogress.uniskinmod.coremod.ILoadSkinFromCache_old;
1617

1718
import java.io.File;
19+
import java.util.Collections;
1820
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.concurrent.ConcurrentHashMap;
1923
import java.util.concurrent.TimeUnit;
2024

2125
@Mod(modid = "uniskinmod")
@@ -85,4 +89,26 @@ public static ResourceLocation getDynamicCapeResource(NetworkPlayerInfo player)
8589
public static ResourceLocation getDynamicElytraResource(NetworkPlayerInfo player) {
8690
return instance.core == null ? null : instance.core.getDynamicElytraResource(player);
8791
}
92+
93+
94+
private static final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> defaultReturn = Collections.emptyMap();
95+
private static final Set<GameProfile> onLoading = Collections.newSetFromMap(new ConcurrentHashMap<GameProfile, Boolean>());
96+
97+
public static Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadSkinFromCache_wrapper(final GameProfile gp, final ILoadSkinFromCache_old skinManager) {
98+
if (onLoading.contains(gp)) return defaultReturn;
99+
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> ret = skinManager.getLoadingCache().getIfPresent(gp);
100+
if (ret == null) {
101+
onLoading.add(gp);
102+
String name = gp.getName() == null ? gp.toString() : gp.getName();
103+
new Thread(new Runnable() {
104+
@Override
105+
public void run() {
106+
skinManager.getLoadingCache().getUnchecked(gp);
107+
onLoading.remove(gp);
108+
}
109+
}, "Skin-Fetch-" + name).start();
110+
return defaultReturn;
111+
}
112+
return ret;
113+
}
88114
}

src/main/java/org/devinprogress/uniskinmod/coremod/AsmTransformer.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,59 @@ public void transform(ClassNode cn, String classObfName, MethodNode mn, String s
229229
mn.instructions.insertBefore(n, new VarInsnNode(Opcodes.ASTORE, 11));
230230
}
231231
}
232+
233+
/*
234+
* Make SkinManager.cache load textures asynchronously
235+
@RegisterTransformer(
236+
className = "net.minecraft.client.resources.SkinManager",
237+
mcpName = "<init>",
238+
desc = "(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/io/File;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)V"
239+
240+
)
241+
public static class delayedLoadingCacheTransformaer implements IMethodTransformer {
242+
@Override
243+
public void transform(ClassNode cn, String classObfName, MethodNode mn, String srgName, boolean devEnv) {
244+
AbstractInsnNode n = getNthInsnNode(mn, Opcodes.PUTFIELD, 4);
245+
mn.instructions.insertBefore(n, new MethodInsnNode(Opcodes.INVOKESTATIC, "org/devinprogress/uniskinmod/coremod/DelayedLoadingCache",
246+
"wrap", "(Lcom/google/common/cache/LoadingCache;)Lcom/google/common/cache/LoadingCache;", false));
247+
}
248+
}
249+
*/
250+
251+
/**
252+
* Make SkinManager.loadSkinFromCache(), which is used by skull
253+
* texture loading, loads textures asynchronously.
254+
* Thus avoids main thread freezing.
255+
*/
256+
@RegisterTransformer(
257+
className = "net.minecraft.client.resources.SkinManager",
258+
mcpName = "loadSkinFromCache",
259+
srgName = "func_152788_a",
260+
desc = "(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;"
261+
)
262+
public static class asyncLoadingCacheTransformer implements IMethodTransformer {
263+
@Override
264+
public void transform(ClassNode cn, String classObfName, MethodNode mn, String srgName, boolean devEnv) {
265+
MethodNode wrapperMethod = new MethodNode(mn.access, mn.name, mn.desc, null,
266+
mn.exceptions.toArray(new String[mn.exceptions.size()]));
267+
mn.name = "loadSkinFromCache_old";
268+
mn.access = Opcodes.ACC_PUBLIC;
269+
270+
wrapperMethod.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
271+
wrapperMethod.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
272+
wrapperMethod.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, INVOKE_TARGET_CLASS, "loadSkinFromCache_wrapper",
273+
"(Lcom/mojang/authlib/GameProfile;Lorg/devinprogress/uniskinmod/coremod/ILoadSkinFromCache_old;)Ljava/util/Map;", false));
274+
wrapperMethod.instructions.add(new InsnNode(Opcodes.ARETURN));
275+
wrapperMethod.maxStack = 3;
276+
277+
cn.methods.add(wrapperMethod);
278+
cn.interfaces.add(ILoadSkinFromCache_old.class.getName().replace(".", "/"));
279+
280+
MethodNode getCacheMethod = new MethodNode(Opcodes.ACC_PUBLIC, "getLoadingCache", "()Lcom/google/common/cache/LoadingCache;", null, new String[0]);
281+
getCacheMethod.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
282+
getCacheMethod.instructions.add(getNthInsnNode(mn, Opcodes.GETFIELD, 1).clone(Collections.<LabelNode, LabelNode>emptyMap()));
283+
getCacheMethod.instructions.add(new InsnNode(Opcodes.ARETURN));
284+
cn.methods.add(getCacheMethod);
285+
}
286+
}
232287
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.devinprogress.uniskinmod.coremod;
2+
3+
import com.google.common.cache.ForwardingLoadingCache;
4+
import com.google.common.cache.LoadingCache;
5+
import com.google.common.util.concurrent.UncheckedExecutionException;
6+
import com.mojang.authlib.GameProfile;
7+
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
8+
9+
import java.util.Collections;
10+
import java.util.Map;
11+
import java.util.Set;
12+
import java.util.concurrent.ConcurrentHashMap;
13+
import java.util.concurrent.ExecutionException;
14+
15+
public class DelayedLoadingCache extends ForwardingLoadingCache.SimpleForwardingLoadingCache<GameProfile, Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> {
16+
public static LoadingCache<GameProfile, Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> wrap(LoadingCache<GameProfile, Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> inside) {
17+
return new DelayedLoadingCache(inside);
18+
}
19+
20+
private DelayedLoadingCache(LoadingCache<GameProfile, Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> inside) {
21+
super(inside);
22+
}
23+
24+
private final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> defaultReturn = Collections.emptyMap();
25+
private Set<GameProfile> onLoading = Collections.newSetFromMap(new ConcurrentHashMap<GameProfile, Boolean>());
26+
27+
@Override
28+
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> get(GameProfile key) throws ExecutionException {
29+
final GameProfile gp = key;
30+
if (onLoading.contains(key)) return defaultReturn;
31+
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> ret = super.getIfPresent(gp);
32+
if (ret == null) {
33+
onLoading.add(gp);
34+
String name = gp.getName() == null ? gp.toString() : gp.getName();
35+
new Thread(new Runnable() {
36+
@Override
37+
public void run() {
38+
DelayedLoadingCache.super.getUnchecked(gp);
39+
onLoading.remove(gp);
40+
}
41+
}, "Skin-Fetch-" + name).start();
42+
return defaultReturn;
43+
}
44+
return ret;
45+
}
46+
47+
@Override
48+
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getUnchecked(GameProfile key) {
49+
try {
50+
return get(key);
51+
} catch (ExecutionException ex) {
52+
throw new UncheckedExecutionException(ex);
53+
}
54+
}
55+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.devinprogress.uniskinmod.coremod;
2+
3+
import com.google.common.cache.LoadingCache;
4+
import com.mojang.authlib.GameProfile;
5+
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
6+
7+
import java.util.Map;
8+
9+
public interface ILoadSkinFromCache_old {
10+
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> loadSkinFromCache_old(GameProfile profile);
11+
12+
LoadingCache<GameProfile, Map<MinecraftProfileTexture.Type, MinecraftProfileTexture>> getLoadingCache();
13+
}

0 commit comments

Comments
 (0)