j’ai réussi:
SkinFMLLoadingPlugin
package pickandcraftSkin;
import java.util.Map;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
@MCVersion(value = "1.6.4")
public class SkinFMLLoadingPlugin implements cpw.mods.fml.relauncher.IFMLLoadingPlugin {
@Override
public String[] getLibraryRequestClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public String[] getASMTransformerClass() {
//This will return the name of the class "mod.culegooner.ExplosionDropsCore.EDClassTransformer"
return new String[]{SkinClassTransformer.class.getName()};
}
@Override
public String getModContainerClass() {
//This is the name of our dummy container "mod.culegooner.ExplosionDropsCore.EDDummyContainer"
return SkinDummyContainer.class.getName();
}
@Override
public String getSetupClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public void injectData(Map <string, object="">data) {
}
}
SkinClassTransformer
package pickandcraftSkin;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import java.util.Iterator;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class SkinClassTransformer implements net.minecraft.launchwrapper.IClassTransformer {
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
//on cherche la classe qui nous interressent. On gère les deux cas: si elle est "obscurcie" ou pas
if (arg0.equals("beu")) {
System.out.println("********* INSIDE OBFUSCATED AbstractClientPlayer TRANSFORMER ABOUT TO PATCH: " + arg0);
return patchClassASM(arg0, arg2, true);
}
if (arg0.equals("net.minecraft.client.entity.AbstractClientPlayer")) {
System.out.println("********* INSIDE AbstractClientPlayer TRANSFORMER ABOUT TO PATCH: " + arg0);
return patchClassASM(arg0, arg2, false);
}
return arg2;
}
public byte[] patchClassASM(String name, byte[] bytes, boolean obfuscated) {
String targetMethod1Name = "", targetMethod2Name = "";
boolean ok1 = false, ok2 = false;
if(obfuscated == true) {
targetMethod1Name ="d";
targetMethod2Name = "e";
}
else {
targetMethod1Name ="getSkinUrl";
targetMethod2Name = "getCapeUrl";
}
//on prépare la manipulation
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
//Maintenant, on boucle sur toutes les méthodes déclarées dans la classe jusqu'à trouver la/les methode(s) cible(s)
Iterator <methodnode>methods = classNode.methods.iterator();
while(methods.hasNext())
{
MethodNode m = methods.next();
System.out.println("********* Method Name: "+m.name + " Desc:" + m.desc);
//On vérifie si il s'agit de la méthode cible et si elle a la même signature (http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html)
if ((m.name.equals(targetMethod1Name) && m.desc.equals("(Ljava/lang/String;)Ljava/lang/String;")))
{
System.out.println("********* Inside target method1!");
InsnList toInject = new InsnList();
toInject.add(new VarInsnNode(ALOAD, 0));
toInject.add(new MethodInsnNode(INVOKESTATIC, "pickandcraftSkin/PlayerCustom", "getURLSkinCustom", "(Ljava/lang/String;)Ljava/lang/String;"));
toInject.add(new InsnNode(ARETURN));
// inject new instruction list into method instruction list
m.instructions = toInject;
ok1 = true;
System.out.println("Patching Method1 Complete!");
}
else if ((m.name.equals(targetMethod2Name) && m.desc.equals("(Ljava/lang/String;)Ljava/lang/String;")))
{
System.out.println("********* Inside target2 method!");
InsnList toInject = new InsnList();
toInject.add(new VarInsnNode(ALOAD, 0));
toInject.add(new MethodInsnNode(INVOKESTATIC, "pickandcraftSkin/PlayerCustom", "getURLCapeCustom", "(Ljava/lang/String;)Ljava/lang/String;"));
toInject.add(new InsnNode(ARETURN));
// inject new instruction list into method instruction list
m.instructions = toInject;
ok2 = true;
System.out.println("Patching Method2 Complete!");
}
if(ok1 && ok2)
break;
}
//ASM specific for cleaning up and returning the final bytes for JVM processing.
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
}
}
PlayerCustom
package pickandcraftSkin;
import java.net.HttpURLConnection;
import java.net.URL;
import net.minecraft.util.StringUtils;
public class PlayerCustom {
private static String connexion (String adress, String par0Str, String part){
//on construit l'adresse URL en ajoutant le pseudo + l'extension de l'image (png)
int code=404;
//on lance une connexion à l'url afin de savoir si il y a un skin d'uploader
try {
URL u = new URL (adress);
HttpURLConnection huc = ( HttpURLConnection ) u.openConnection ();
huc.setRequestMethod ("GET");
huc.connect () ;
//on récupère le code HTTP retourné par le serveur
code = huc.getResponseCode() ;
System.out.println("SKINURL:code "+code);
//si il y est correct on retourne l'adresse
if (code == HttpURLConnection.HTTP_OK)
return adress;
//sinon si il y a une redirection on teste avec la nouvelle url. Bien sur il faudra retourné en php une chaine de texte formaté avec Url du site + le pseudo du joueur + l'extension mais ce n'est pas l'objet de ce tutoriel
else if (code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER) {
//on récupère la nouvelle adresse
String newUrl = huc.getHeaderField("Location");
// et on lance une connection avec la nouvelle URL
huc = (HttpURLConnection) new URL(newUrl).openConnection();
huc.setRequestMethod ("GET");
huc.connect () ;
code = huc.getResponseCode() ;
System.out.println("SKINURL:code "+code);
//si cette fois le serveur renvoie un code 200 alors on retourne la nouvelle adresse
if (code == HttpURLConnection.HTTP_OK)
return newUrl;
}
}
catch(Exception e){
System.out.println("SKINURL:Bad Url.");
}
//si finalement on ne reçoit aucune réponse 200 on retourne l'adresse par défaut
return String.format("http://skins.minecraft.net/Minecraft"+ part + "/%s.png", new Object[] {StringUtils.stripControlCodes(par0Str)});
}
public static String getURLSkinCustom(String par0Str)
{
String adress=String.format("http://monsite/skins/%s.png", new Object[] {StringUtils.stripControlCodes(par0Str)});
return connexion(adress, par0Str, "Skins");
}
public static String getURLCapeCustom(String par0Str)
{
String adress=String.format("http://monsite/capes/%s.png", new Object[] {StringUtils.stripControlCodes(par0Str)});
return connexion(adress, par0Str, "Cloaks");
}
}
```</methodnode></string,>