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

Commit 4e0de2a

Browse files
committed
1.9 update
1 parent 2f1c692 commit 4e0de2a

File tree

8 files changed

+141
-71
lines changed

8 files changed

+141
-71
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Current version is v1.4-alpha2 for Minecraft 1.9
1717
### Configure
1818

1919
- The config file is located in `config/UniSkinMod/UniSkinMod.json`
20+
- Local skin folder is `config/UniSkinMod/local_skins/`. You have to create it manually.
2021
- For more info about the "Root" URL, visit [UniSkinAPI Document](https://github.com/RecursiveG/UniSkinServer/blob/master/doc/UniSkinAPI_en.md) please!
2122

2223
### License

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ processResources
5353
exclude 'mcmod.info'
5454
}
5555
}
56+

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

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77
import net.minecraft.client.Minecraft;
88
import net.minecraft.client.resources.SkinManager;
99
import net.minecraft.util.ResourceLocation;
10+
import org.apache.commons.codec.digest.DigestUtils;
11+
import org.apache.commons.io.FileUtils;
12+
import org.apache.logging.log4j.Level;
1013

1114
import java.io.File;
15+
import java.io.FileInputStream;
1216
import java.util.ArrayList;
1317
import java.util.HashMap;
1418
import java.util.List;
19+
import java.util.Map;
1520
import java.util.concurrent.TimeUnit;
1621

1722
public class DynamicSkinManager {
1823
private final List<String> rootURIs;
1924
private final File localSkinDir;
25+
private final File mcSkinCacheDir;
2026

2127
public class CachedDynamicSkin {
2228
ResourceLocation[] skin = null;
@@ -33,9 +39,10 @@ public boolean complete() {
3339
}
3440
}
3541

36-
public DynamicSkinManager(List<String> r, File localSkin) {
42+
public DynamicSkinManager(List<String> r, File localSkin, File mcCache) {
3743
rootURIs = r;
3844
localSkinDir = localSkin;
45+
mcSkinCacheDir = mcCache;
3946
}
4047

4148
public final LoadingCache<String, CachedDynamicSkin> cache = CacheBuilder.newBuilder()
@@ -111,23 +118,60 @@ private void forceLoadTextures(CachedDynamicSkin cache) {
111118
if (cache.skinURL != null) {
112119
boolean slimModel = cache.model.equalsIgnoreCase("slim");
113120
for (int i = 0; i < cache.skinURL.length; i++) {
114-
skinManager.loadSkin(new MinecraftProfileTexture(cache.skinURL[i], slimModel ?
115-
new HashMap<String, String>() {{
116-
put("model", "slim");
117-
}} : null), MinecraftProfileTexture.Type.SKIN);
121+
String url = cache.skinURL[i];
122+
if (url.startsWith("local:")) {
123+
File src = new File(url.substring(6));
124+
forceLoadTexture(src, MinecraftProfileTexture.Type.SKIN, slimModel);
125+
} else {
126+
skinManager.loadSkin(new MinecraftProfileTexture(url, slimModel ?
127+
new HashMap<String, String>() {{
128+
put("model", "slim");
129+
}} : null), MinecraftProfileTexture.Type.SKIN);
130+
}
118131
}
119132
}
120133

121134
if (cache.capeURL != null) {
122135
for (int i = 0; i < cache.capeURL.length; i++) {
123-
skinManager.loadSkin(new MinecraftProfileTexture(cache.capeURL[i], null), MinecraftProfileTexture.Type.CAPE);
136+
String url = cache.capeURL[i];
137+
if (url.startsWith("local:")) {
138+
forceLoadTexture(new File(url.substring(6)), MinecraftProfileTexture.Type.CAPE, false);
139+
} else {
140+
skinManager.loadSkin(new MinecraftProfileTexture(url, null), MinecraftProfileTexture.Type.CAPE);
141+
}
124142
}
125143
}
126144

127145
if (cache.elytraURL != null) {
128146
for (int i = 0; i < cache.elytraURL.length; i++) {
129-
skinManager.loadSkin(new MinecraftProfileTexture(cache.elytraURL[i], null), MinecraftProfileTexture.Type.ELYTRA);
147+
String url = cache.elytraURL[i];
148+
if (url.startsWith("local:")) {
149+
forceLoadTexture(new File(url.substring(6)), MinecraftProfileTexture.Type.ELYTRA, false);
150+
} else {
151+
skinManager.loadSkin(new MinecraftProfileTexture(url, null), MinecraftProfileTexture.Type.ELYTRA);
152+
}
130153
}
131154
}
132155
}
156+
157+
public void forceLoadTexture(File sourceFile, MinecraftProfileTexture.Type textureType, boolean isAlex) {
158+
try {
159+
File dstFolder = this.mcSkinCacheDir;
160+
String sha256 = DigestUtils.sha256Hex(new FileInputStream(sourceFile)).toLowerCase();
161+
String dir = sha256.substring(0, 2);
162+
File subDir = new File(dstFolder, dir);
163+
subDir.mkdirs();
164+
File dstFile = new File(subDir, sha256);
165+
FileUtils.copyFile(sourceFile, dstFile);
166+
Map<String, String> metadata = (textureType == MinecraftProfileTexture.Type.SKIN && isAlex)
167+
? new HashMap<String, String>() {{
168+
put("model", "slim");
169+
}} : null;
170+
String url = "http://127.0.0.1/" + sha256;
171+
Minecraft.getMinecraft().getSkinManager().loadSkin(
172+
new MinecraftProfileTexture(url, metadata), textureType);
173+
} catch (Exception ex) {
174+
UniSkinMod.log.catching(Level.WARN, ex);
175+
}
176+
}
133177
}

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

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,12 @@
2020
import java.io.File;
2121
import java.io.IOException;
2222
import java.io.InputStream;
23+
import java.lang.reflect.Field;
2324
import java.net.URL;
2425
import java.net.URLConnection;
2526
import java.util.UUID;
2627
import java.util.concurrent.ExecutionException;
2728

28-
/* These imports require forge and provided support for dynamic skins
29-
* Comment them out if compiled as a standalone library.
30-
*/
31-
3229
/**
3330
* Provided the interface to interact with Mojang codes
3431
*/
@@ -51,7 +48,24 @@ public UniSkinCore(UniSkinConfig configuration, File localSkin) {
5148
for (String str : cfg.legacyCapeURIs) UniSkinMod.log.info("Added Cape URI: {}", str);
5249
mojangProfileRepo = new YggdrasilAuthenticationService(Minecraft.getMinecraft().getProxy(), UUID.randomUUID().toString())
5350
.createProfileRepository();
54-
dynamicSkinManager = new DynamicSkinManager(cfg.rootURIs, localSkin);
51+
52+
File assertDir;
53+
try {
54+
Field f;
55+
try {
56+
f = Minecraft.class.getDeclaredField("field_110446_Y");
57+
} catch (NoSuchFieldException ex) {
58+
f = null;
59+
}
60+
if (f == null) f = Minecraft.class.getDeclaredField("fileAssets");
61+
f.setAccessible(true);
62+
Object obj = f.get(Minecraft.getMinecraft());
63+
assertDir = (File) obj;
64+
} catch (ReflectiveOperationException ex) {
65+
throw new RuntimeException("Unable to determine skin cache dir.", ex);
66+
}
67+
if (assertDir == null) throw new RuntimeException("Unable to determine skin cache dir.");
68+
dynamicSkinManager = new DynamicSkinManager(cfg.rootURIs, localSkin, new File(assertDir, "skins"));
5569
}
5670

5771
public void injectProfile(GameProfile profile) {
@@ -219,7 +233,8 @@ private void injectLegacyProfile(GameProfile profile) {
219233
try {
220234
File localFile = new File(localTexture, hash);
221235
FileUtils.writeByteArrayToFile(localFile, skinData);
222-
payload.addSkin(localFile.toURI().toString(), "default");
236+
dynamicSkinManager.forceLoadTexture(localFile, MinecraftProfileTexture.Type.SKIN, false);
237+
payload.addSkin("http://127.0.0.1/" + hash, "default");
223238
UniSkinMod.log.info("Injecting legacy skin: {} {}", playerName, hash);
224239
} catch (IOException ex) {
225240
UniSkinMod.log.catching(Level.WARN, ex);
@@ -240,7 +255,8 @@ private void injectLegacyProfile(GameProfile profile) {
240255
try {
241256
File localFile = new File(localTexture, hash);
242257
FileUtils.writeByteArrayToFile(localFile, capeData);
243-
payload.addCape(localFile.toURI().toString());
258+
dynamicSkinManager.forceLoadTexture(localFile, MinecraftProfileTexture.Type.CAPE, false);
259+
payload.addCape("http://127.0.0.1/" + hash);
244260
UniSkinMod.log.info("Injecting legacy cape: {} {}", playerName, hash);
245261
} catch (IOException ex) {
246262
UniSkinMod.log.catching(Level.WARN, ex);
@@ -251,10 +267,10 @@ private void injectLegacyProfile(GameProfile profile) {
251267
payload.dumpIntoGameProfile(profile);
252268
}
253269

270+
254271
/* Codes below require forge and provided support for dynamic skins
255272
* Comment them out if compiled as a standalone library. */
256273

257-
258274
/**
259275
* called from AbstractClientPlayer.getLocationSkin()
260276
*/
@@ -264,7 +280,6 @@ public ResourceLocation getDynamicSkinResource(NetworkPlayerInfo player) {
264280

265281
/**
266282
* called from TileEntitySkllRenderer.renderSkull()
267-
* <pre>resourcelocation = UniSkinMod.getDynamicSkinResourceForSkull(p_188190_7_,DefaultPlayerSkin.getDefaultSkin(uuid));</pre>
268283
*/
269284
public ResourceLocation getDynamicSkinResourceForSkull(GameProfile gp, ResourceLocation def) {
270285
if (gp == null) return def;
@@ -278,6 +293,8 @@ public ResourceLocation getDynamicSkinResourceForSkull(GameProfile gp, ResourceL
278293
return s.skin[id];
279294
}
280295
} catch (ExecutionException ex) {
296+
UniSkinMod.log.catching(Level.WARN, ex);
297+
return def;
281298
}
282299
return def;
283300
}
@@ -297,6 +314,8 @@ public String getDynamicSkinModel(NetworkPlayerInfo player) {
297314
return s.model;
298315
}
299316
} catch (ExecutionException ex) {
317+
UniSkinMod.log.catching(Level.WARN, ex);
318+
return null;
300319
}
301320
}
302321
}
@@ -315,11 +334,13 @@ public ResourceLocation getDynamicCapeResource(NetworkPlayerInfo player) {
315334
try {
316335
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.get(name);
317336
if (s.cape != null && s.cape.length != 0) {
318-
double spf = (double) s.skinInterval / (double) s.skin.length;
337+
double spf = (double) s.capeInterval / (double) s.cape.length;
319338
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.capeInterval) / spf)) % (s.cape.length);
320339
return s.cape[id];
321340
}
322341
} catch (ExecutionException ex) {
342+
UniSkinMod.log.catching(Level.WARN, ex);
343+
return null;
323344
}
324345
}
325346
}
@@ -338,11 +359,13 @@ public ResourceLocation getDynamicElytraResource(NetworkPlayerInfo player) {
338359
try {
339360
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.get(name);
340361
if (s.elytra != null && s.elytra.length != 0) {
341-
double spf = (double) s.skinInterval / (double) s.skin.length;
362+
double spf = (double) s.elytraInterval / (double) s.elytra.length;
342363
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.elytraInterval) / spf)) % (s.elytra.length);
343364
return s.elytra[id];
344365
}
345366
} catch (ExecutionException ex) {
367+
UniSkinMod.log.catching(Level.WARN, ex);
368+
return null;
346369
}
347370
}
348371
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.common.cache.CacheBuilder;
66
import com.google.gson.Gson;
77
import net.minecraft.client.Minecraft;
8+
import org.apache.commons.io.FilenameUtils;
89
import org.apache.commons.io.IOUtils;
910
import org.apache.logging.log4j.Level;
1011

@@ -239,6 +240,7 @@ public ProfileJSON call() throws Exception {
239240
}
240241

241242
private UniSkinProfile(File profileFile, File textureFolder) {
243+
String name = FilenameUtils.removeExtension(profileFile.getName());
242244
ProfileJSON json;
243245
try {
244246
json = (new Gson()).fromJson(new FileReader(profileFile), ProfileJSON.class);
@@ -250,7 +252,7 @@ private UniSkinProfile(File profileFile, File textureFolder) {
250252
for (String m : json.model_preference) {
251253
if (json.skins.containsKey(m) && (m.equals("default") || m.equals("slim"))) {
252254
model = m;
253-
skin = new File(textureFolder, json.skins.get(m)).toURI().toString();
255+
skin = "local:" + (new File(textureFolder, json.skins.get(m)).getAbsolutePath());
254256
hasProfile = true;
255257
UniSkinMod.log.info("Player Skin Selected: {} {} {}", name, model, json.skins.get(m));
256258
break;
@@ -259,19 +261,19 @@ private UniSkinProfile(File profileFile, File textureFolder) {
259261

260262
if (json.model_preference.contains("cape") && json.skins.containsKey("cape")) {
261263
String tmp = json.skins.get("cape");
262-
cape = new File(textureFolder, tmp).toURI().toString();
264+
cape = "local:" + (new File(textureFolder, tmp).getAbsolutePath());
263265
hasProfile = true;
264266
UniSkinMod.log.info("Player Cape Selected: {} {}", name, tmp);
265267
}
266268
if (json.cape != null && json.cape.length() > 3 && cape != null) {
267-
cape = new File(textureFolder, json.cape).toURI().toString();
269+
cape = "local:" + (new File(textureFolder, json.cape).getAbsolutePath());
268270
hasProfile = true;
269271
UniSkinMod.log.info("Player Cape Selected: {} {}", name, json.cape);
270272
}
271273

272274
if (json.model_preference.contains("elytra") && json.skins.containsKey("elytra")) {
273275
String tmp = json.skins.get("elytra");
274-
elytra = new File(textureFolder, tmp).toURI().toString();
276+
elytra = "local:" + (new File(textureFolder, tmp).getAbsolutePath());
275277
hasProfile = true;
276278
UniSkinMod.log.info("Player Elytra Selected: {} {}", name, tmp);
277279
}
@@ -296,7 +298,9 @@ private UniSkinProfile(File profileFile, File textureFolder) {
296298
if (time <= 0) continue;
297299
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
298300
String[] urls = new String[hashes.length];
299-
for (int i = 0; i < urls.length; i++) urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
301+
for (int i = 0; i < urls.length; i++) {
302+
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
303+
}
300304
dynSkin = new DynamicTexture(time, hashes, model, urls);
301305
hasProfile = true;
302306
UniSkinMod.log.info("Dynamic Skin Selected: {}", name);
@@ -320,7 +324,7 @@ private UniSkinProfile(File profileFile, File textureFolder) {
320324
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
321325
String[] urls = new String[hashes.length];
322326
for (int i = 0; i < urls.length; i++)
323-
urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
327+
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
324328
dynCape = new DynamicTexture(time, hashes, null, urls);
325329
hasProfile = true;
326330
UniSkinMod.log.info("Dynamic Cape Selected: {}", name);
@@ -343,7 +347,7 @@ private UniSkinProfile(File profileFile, File textureFolder) {
343347
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
344348
String[] urls = new String[hashes.length];
345349
for (int i = 0; i < urls.length; i++)
346-
urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
350+
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
347351
dynElytra = new DynamicTexture(time, hashes, null, urls);
348352
hasProfile = true;
349353
UniSkinMod.log.info("Dynamic Elytra Selected: {}", name);

0 commit comments

Comments
 (0)