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

Commit

Permalink
1.9 update
Browse files Browse the repository at this point in the history
  • Loading branch information
RecursiveG committed Mar 20, 2016
1 parent 2f1c692 commit 4e0de2a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 71 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Current version is v1.4-alpha2 for Minecraft 1.9
### Configure

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

### License
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ processResources
exclude 'mcmod.info'
}
}

58 changes: 51 additions & 7 deletions src/main/java/org/devinprogress/uniskinmod/DynamicSkinManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.SkinManager;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Level;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class DynamicSkinManager {
private final List<String> rootURIs;
private final File localSkinDir;
private final File mcSkinCacheDir;

public class CachedDynamicSkin {
ResourceLocation[] skin = null;
Expand All @@ -33,9 +39,10 @@ public boolean complete() {
}
}

public DynamicSkinManager(List<String> r, File localSkin) {
public DynamicSkinManager(List<String> r, File localSkin, File mcCache) {
rootURIs = r;
localSkinDir = localSkin;
mcSkinCacheDir = mcCache;
}

public final LoadingCache<String, CachedDynamicSkin> cache = CacheBuilder.newBuilder()
Expand Down Expand Up @@ -111,23 +118,60 @@ private void forceLoadTextures(CachedDynamicSkin cache) {
if (cache.skinURL != null) {
boolean slimModel = cache.model.equalsIgnoreCase("slim");
for (int i = 0; i < cache.skinURL.length; i++) {
skinManager.loadSkin(new MinecraftProfileTexture(cache.skinURL[i], slimModel ?
new HashMap<String, String>() {{
put("model", "slim");
}} : null), MinecraftProfileTexture.Type.SKIN);
String url = cache.skinURL[i];
if (url.startsWith("local:")) {
File src = new File(url.substring(6));
forceLoadTexture(src, MinecraftProfileTexture.Type.SKIN, slimModel);
} else {
skinManager.loadSkin(new MinecraftProfileTexture(url, slimModel ?
new HashMap<String, String>() {{
put("model", "slim");
}} : null), MinecraftProfileTexture.Type.SKIN);
}
}
}

if (cache.capeURL != null) {
for (int i = 0; i < cache.capeURL.length; i++) {
skinManager.loadSkin(new MinecraftProfileTexture(cache.capeURL[i], null), MinecraftProfileTexture.Type.CAPE);
String url = cache.capeURL[i];
if (url.startsWith("local:")) {
forceLoadTexture(new File(url.substring(6)), MinecraftProfileTexture.Type.CAPE, false);
} else {
skinManager.loadSkin(new MinecraftProfileTexture(url, null), MinecraftProfileTexture.Type.CAPE);
}
}
}

if (cache.elytraURL != null) {
for (int i = 0; i < cache.elytraURL.length; i++) {
skinManager.loadSkin(new MinecraftProfileTexture(cache.elytraURL[i], null), MinecraftProfileTexture.Type.ELYTRA);
String url = cache.elytraURL[i];
if (url.startsWith("local:")) {
forceLoadTexture(new File(url.substring(6)), MinecraftProfileTexture.Type.ELYTRA, false);
} else {
skinManager.loadSkin(new MinecraftProfileTexture(url, null), MinecraftProfileTexture.Type.ELYTRA);
}
}
}
}

public void forceLoadTexture(File sourceFile, MinecraftProfileTexture.Type textureType, boolean isAlex) {
try {
File dstFolder = this.mcSkinCacheDir;
String sha256 = DigestUtils.sha256Hex(new FileInputStream(sourceFile)).toLowerCase();
String dir = sha256.substring(0, 2);
File subDir = new File(dstFolder, dir);
subDir.mkdirs();
File dstFile = new File(subDir, sha256);
FileUtils.copyFile(sourceFile, dstFile);
Map<String, String> metadata = (textureType == MinecraftProfileTexture.Type.SKIN && isAlex)
? new HashMap<String, String>() {{
put("model", "slim");
}} : null;
String url = "http://127.0.0.1/" + sha256;
Minecraft.getMinecraft().getSkinManager().loadSkin(
new MinecraftProfileTexture(url, metadata), textureType);
} catch (Exception ex) {
UniSkinMod.log.catching(Level.WARN, ex);
}
}
}
45 changes: 34 additions & 11 deletions src/main/java/org/devinprogress/uniskinmod/UniSkinCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

/* These imports require forge and provided support for dynamic skins
* Comment them out if compiled as a standalone library.
*/

/**
* Provided the interface to interact with Mojang codes
*/
Expand All @@ -51,7 +48,24 @@ public UniSkinCore(UniSkinConfig configuration, File localSkin) {
for (String str : cfg.legacyCapeURIs) UniSkinMod.log.info("Added Cape URI: {}", str);
mojangProfileRepo = new YggdrasilAuthenticationService(Minecraft.getMinecraft().getProxy(), UUID.randomUUID().toString())
.createProfileRepository();
dynamicSkinManager = new DynamicSkinManager(cfg.rootURIs, localSkin);

File assertDir;
try {
Field f;
try {
f = Minecraft.class.getDeclaredField("field_110446_Y");
} catch (NoSuchFieldException ex) {
f = null;
}
if (f == null) f = Minecraft.class.getDeclaredField("fileAssets");
f.setAccessible(true);
Object obj = f.get(Minecraft.getMinecraft());
assertDir = (File) obj;
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Unable to determine skin cache dir.", ex);
}
if (assertDir == null) throw new RuntimeException("Unable to determine skin cache dir.");
dynamicSkinManager = new DynamicSkinManager(cfg.rootURIs, localSkin, new File(assertDir, "skins"));
}

public void injectProfile(GameProfile profile) {
Expand Down Expand Up @@ -219,7 +233,8 @@ private void injectLegacyProfile(GameProfile profile) {
try {
File localFile = new File(localTexture, hash);
FileUtils.writeByteArrayToFile(localFile, skinData);
payload.addSkin(localFile.toURI().toString(), "default");
dynamicSkinManager.forceLoadTexture(localFile, MinecraftProfileTexture.Type.SKIN, false);
payload.addSkin("http://127.0.0.1/" + hash, "default");
UniSkinMod.log.info("Injecting legacy skin: {} {}", playerName, hash);
} catch (IOException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
Expand All @@ -240,7 +255,8 @@ private void injectLegacyProfile(GameProfile profile) {
try {
File localFile = new File(localTexture, hash);
FileUtils.writeByteArrayToFile(localFile, capeData);
payload.addCape(localFile.toURI().toString());
dynamicSkinManager.forceLoadTexture(localFile, MinecraftProfileTexture.Type.CAPE, false);
payload.addCape("http://127.0.0.1/" + hash);
UniSkinMod.log.info("Injecting legacy cape: {} {}", playerName, hash);
} catch (IOException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
Expand All @@ -251,10 +267,10 @@ private void injectLegacyProfile(GameProfile profile) {
payload.dumpIntoGameProfile(profile);
}


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


/**
* called from AbstractClientPlayer.getLocationSkin()
*/
Expand All @@ -264,7 +280,6 @@ public ResourceLocation getDynamicSkinResource(NetworkPlayerInfo player) {

/**
* called from TileEntitySkllRenderer.renderSkull()
* <pre>resourcelocation = UniSkinMod.getDynamicSkinResourceForSkull(p_188190_7_,DefaultPlayerSkin.getDefaultSkin(uuid));</pre>
*/
public ResourceLocation getDynamicSkinResourceForSkull(GameProfile gp, ResourceLocation def) {
if (gp == null) return def;
Expand All @@ -278,6 +293,8 @@ public ResourceLocation getDynamicSkinResourceForSkull(GameProfile gp, ResourceL
return s.skin[id];
}
} catch (ExecutionException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
return def;
}
return def;
}
Expand All @@ -297,6 +314,8 @@ public String getDynamicSkinModel(NetworkPlayerInfo player) {
return s.model;
}
} catch (ExecutionException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
return null;
}
}
}
Expand All @@ -315,11 +334,13 @@ public ResourceLocation getDynamicCapeResource(NetworkPlayerInfo player) {
try {
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.get(name);
if (s.cape != null && s.cape.length != 0) {
double spf = (double) s.skinInterval / (double) s.skin.length;
double spf = (double) s.capeInterval / (double) s.cape.length;
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.capeInterval) / spf)) % (s.cape.length);
return s.cape[id];
}
} catch (ExecutionException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
return null;
}
}
}
Expand All @@ -338,11 +359,13 @@ public ResourceLocation getDynamicElytraResource(NetworkPlayerInfo player) {
try {
DynamicSkinManager.CachedDynamicSkin s = dynamicSkinManager.cache.get(name);
if (s.elytra != null && s.elytra.length != 0) {
double spf = (double) s.skinInterval / (double) s.skin.length;
double spf = (double) s.elytraInterval / (double) s.elytra.length;
int id = ((int) Math.floor((double) (System.currentTimeMillis() % s.elytraInterval) / spf)) % (s.elytra.length);
return s.elytra[id];
}
} catch (ExecutionException ex) {
UniSkinMod.log.catching(Level.WARN, ex);
return null;
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions src/main/java/org/devinprogress/uniskinmod/UniSkinProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.common.cache.CacheBuilder;
import com.google.gson.Gson;
import net.minecraft.client.Minecraft;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Level;

Expand Down Expand Up @@ -239,6 +240,7 @@ public ProfileJSON call() throws Exception {
}

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

if (json.model_preference.contains("cape") && json.skins.containsKey("cape")) {
String tmp = json.skins.get("cape");
cape = new File(textureFolder, tmp).toURI().toString();
cape = "local:" + (new File(textureFolder, tmp).getAbsolutePath());
hasProfile = true;
UniSkinMod.log.info("Player Cape Selected: {} {}", name, tmp);
}
if (json.cape != null && json.cape.length() > 3 && cape != null) {
cape = new File(textureFolder, json.cape).toURI().toString();
cape = "local:" + (new File(textureFolder, json.cape).getAbsolutePath());
hasProfile = true;
UniSkinMod.log.info("Player Cape Selected: {} {}", name, json.cape);
}

if (json.model_preference.contains("elytra") && json.skins.containsKey("elytra")) {
String tmp = json.skins.get("elytra");
elytra = new File(textureFolder, tmp).toURI().toString();
elytra = "local:" + (new File(textureFolder, tmp).getAbsolutePath());
hasProfile = true;
UniSkinMod.log.info("Player Elytra Selected: {} {}", name, tmp);
}
Expand All @@ -296,7 +298,9 @@ private UniSkinProfile(File profileFile, File textureFolder) {
if (time <= 0) continue;
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
String[] urls = new String[hashes.length];
for (int i = 0; i < urls.length; i++) urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
for (int i = 0; i < urls.length; i++) {
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
}
dynSkin = new DynamicTexture(time, hashes, model, urls);
hasProfile = true;
UniSkinMod.log.info("Dynamic Skin Selected: {}", name);
Expand All @@ -320,7 +324,7 @@ private UniSkinProfile(File profileFile, File textureFolder) {
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
String[] urls = new String[hashes.length];
for (int i = 0; i < urls.length; i++)
urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
dynCape = new DynamicTexture(time, hashes, null, urls);
hasProfile = true;
UniSkinMod.log.info("Dynamic Cape Selected: {}", name);
Expand All @@ -343,7 +347,7 @@ private UniSkinProfile(File profileFile, File textureFolder) {
String[] hashes = tmp.substring(tmp.indexOf(",") + 1).split(",");
String[] urls = new String[hashes.length];
for (int i = 0; i < urls.length; i++)
urls[i] = new File(textureFolder, hashes[i]).toURI().toString();
urls[i] = "local:" + (new File(textureFolder, hashes[i]).getAbsolutePath());
dynElytra = new DynamicTexture(time, hashes, null, urls);
hasProfile = true;
UniSkinMod.log.info("Dynamic Elytra Selected: {}", name);
Expand Down
Loading

0 comments on commit 4e0de2a

Please sign in to comment.