Problème coremod TEChest IncompatibleClassChangeError



  • Bonjour, bonsoir, j'ai un petit mod à faire qui consisterait à faire pourrir de la viande, même stockée dans les coffres. J'ai eu alors l'idée de "modifier" la fonction updateEntity de la classe TileEntityChest. Pour ce faire je me suis servi d'un class transformer. Le problème est que je ne pense pas m'être trompé, j'ai l'habitude de cette manoeuvre, or une fois en jeu, j'ai ce crash-là 😕

    java.lang.IncompatibleClassChangeError: Expected static method fr.plaigon.rottingmeat.common.coremod.Patch.updateEntityPatch(Lnet/minecraft/tileentity/TileEntityChest;)V
    at net.minecraft.tileentity.TileEntityChest.updateEntity(TileEntityChest.java)
    at net.minecraft.world.World.updateEntities(World.java:2160)
    at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:515)
    at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:703)
    at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614)
    at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485)
    at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752)
    

    J'ai en outre utilisé un _at.cfg pour passer le field TileEntityChest#ticksSinceSync à public. Et je me suis renseigné sur l'exception en question, il se pourrait que ça vienne de là ? Du coup j'hésite à utiliser la classe ObfuscationReflectionHelper qui pourrait probablement éviter l'erreur, nan ?
    Bref voici mes classes et mon build.gradle :

    build.gradle :

    buildscript {
       repositories {
           mavenCentral()
           maven {
               name = "forge"
               url = "http://files.minecraftforge.net/maven"
           }
           maven {
               name = "sonatype"
               url = "https://oss.sonatype.org/content/repositories/snapshots/"
           }
       }
       dependencies {
           classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
       }
    }
    
    apply plugin: 'forge'
    
    version = "1.0"
    group= "com.yourname.modid" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
    archivesBaseName = "modid"
    
    minecraft {
       version = "1.7.10-10.13.4.1558-1.7.10"
       runDir = "eclipse"
    }
    
    dependencies {
       // you may put jars on which you depend on in ./libs
       // or you may define them like so..
       //compile "some.group:artifact:version:classifier"
       //compile "some.group:artifact:version"
    
       // real examples
       //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev'  // adds buildcraft to the dev env
       //compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
    
       // for more info…
       // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
       // http://www.gradle.org/docs/current/userguide/dependency_management.html
    
    }
    
    jar {
       manifest {
           attributes("FMLCorePlugin": "fr.plaigon.rottingmeat.common.coremod.RottingMeatLoadingPlugin", "FMLCorePluginContainsFMLMod": "true", 'FMLAT': 'rottingmeat_at.cfg')
       }
    }
    runServer {
       jvmArgs '-Dfml.coreMods.load=fr.plaigon.rottingmeat.common.coremod.RottingMeatLoadingPlugin'
    }
    
    runClient {
       jvmArgs '-Dfml.coreMods.load=fr.plaigon.rottingmeat.common.coremod.RottingMeatLoadingPlugin'
    }
    
    processResources
    {
       // this will ensure that this task is redone when the versions change.
       inputs.property "version", project.version
       inputs.property "mcversion", project.minecraft.version
    
       // replace stuff in mcmod.info, nothing else
       from(sourceSets.main.resources.srcDirs) {
           include 'mcmod.info'
    
           // replace version and mcversion
           expand 'version':project.version, 'mcversion':project.minecraft.version
       }
    
       // copy everything else, thats not the mcmod.info
       from(sourceSets.main.resources.srcDirs) {
           exclude 'mcmod.info'
       }
    }
    

    RottingMeatLoadingPlugin.java :

    
    package fr.plaigon.rottingmeat.common.coremod;
    
    import java.util.Map;
    
    import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
    
    @MCVersion("1.7.10")
    public class RottingMeatLoadingPlugin implements
    cpw.mods.fml.relauncher.IFMLLoadingPlugin
    {
    
    public static boolean obfuscation = false;
    
    @Override
    public String[] getASMTransformerClass() {
    return new String[] {TileEntityChestTransformer.class.getName() };
    }
    
    @Override
    public String getModContainerClass() {
    return null;
    }
    
    @Override
    public String getSetupClass() {
    
    return null;
    }
    
    @Override
    public void injectData(Map <string, object="">data) {
    }
    
    @Override
    public String getAccessTransformerClass() {
    return null;
    }
    }
    
    

    TileEntityChestTransformer.java :

    
    package fr.plaigon.rottingmeat.common.coremod;
    
    import java.util.ArrayList;
    
    import org.objectweb.asm.ClassReader;
    import org.objectweb.asm.ClassWriter;
    import org.objectweb.asm.Opcodes;
    import org.objectweb.asm.tree.ClassNode;
    import org.objectweb.asm.tree.InsnList;
    import org.objectweb.asm.tree.InsnNode;
    import org.objectweb.asm.tree.LocalVariableNode;
    import org.objectweb.asm.tree.MethodInsnNode;
    import org.objectweb.asm.tree.MethodNode;
    import org.objectweb.asm.tree.VarInsnNode;
    
    import net.minecraft.launchwrapper.IClassTransformer;
    
    public class TileEntityChestTransformer implements IClassTransformer
    {
    private String methodName;
    private static String className, superclassName;
    
    @Override
    public byte[] transform(String name, String transformedName, byte[] basicClass)
    {
    if (name.equals("net.minecraft.profiler.IPlayerUsage"))
    {
    RottingMeatLoadingPlugin.obfuscation = false;
    }
    if (name.equals("rk"))
    {
    RottingMeatLoadingPlugin.obfuscation = true;
    }
    if (transformedName.equals("net.minecraft.tileentity.TileEntityChest"))
    {
    System.out.println("Rotting Meat Core - Patching class TileEntityChest…");
    methodName = RottingMeatLoadingPlugin.obfuscation ? "h" : "updateEntity";
    className = RottingMeatLoadingPlugin.obfuscation ? "aow" : "net/minecraft/tileentity/TileEntityChest";
    superclassName = RottingMeatLoadingPlugin.obfuscation ? "aor" : "net/minecraft/tileentity/TileEntity";
    
    ClassReader cr = new ClassReader(basicClass);
    ClassNode cn = new ClassNode(Opcodes.ASM4);
    cr.accept(cn, 0);
    for (Object mnObj : cn.methods) 
    {
    MethodNode mn = (MethodNode) mnObj;
    if (mn.name.equals(methodName) && mn.desc.equals("()V"))
    {
    patchMethod(mn);
    }
    }
    ClassWriter cw = new ClassWriter(0);
    cn.accept(cw);
    
    System.out.println("Rotting Meat Core - Patching class TileEntityChest done.");
    return cw.toByteArray();
    }
    else
    {
    return basicClass;
    }
    }
    
    private static void patchMethod(MethodNode mn)
    {
    System.out.println("\tPatching method processPlayer in TileEntityChest");
    InsnList newList = new InsnList();
    
    mn.localVariables = new ArrayList<localvariablenode>(5);
    
    newList.add(new VarInsnNode(Opcodes.ALOAD, 0));
    newList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "fr/plaigon/rottingmeat/common/coremod/Patch", "updateEntityPatch", "(L" + className + ";)V", false));
    newList.add(new InsnNode(Opcodes.RETURN));
    mn.instructions = newList;
    }
    }
    
    

    Patch.java :

    
    package fr.plaigon.rottingmeat.common.coremod;
    
    import java.util.Iterator;
    import java.util.List;
    
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.inventory.ContainerChest;
    import net.minecraft.inventory.IInventory;
    import net.minecraft.inventory.InventoryLargeChest;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.tileentity.TileEntityChest;
    import net.minecraft.util.AxisAlignedBB;
    
    public class Patch
    {
    public void updateEntityPatch(TileEntityChest teChest)
        {
    //        te.updateEntity();
            teChest.checkForAdjacentChests();
            ++teChest.ticksSinceSync;
            float f;
    
            if (!teChest.getWorldObj().isRemote && teChest.numPlayersUsing != 0 && (teChest.ticksSinceSync + teChest.xCoord + teChest.yCoord + teChest.zCoord) % 200 == 0)
            {
                teChest.numPlayersUsing = 0;
                f = 5.0F;
                List list = teChest.getWorldObj().getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox((double)((float)teChest.xCoord - f), (double)((float)teChest.yCoord - f), (double)((float)teChest.zCoord - f), (double)((float)(teChest.xCoord + 1) + f), (double)((float)(teChest.yCoord + 1) + f), (double)((float)(teChest.zCoord + 1) + f)));
                Iterator iterator = list.iterator();
    
                while (iterator.hasNext())
                {
                    EntityPlayer entityplayer = (EntityPlayer)iterator.next();
    
                    if (entityplayer.openContainer instanceof ContainerChest)
                    {
                        IInventory iinventory = ((ContainerChest)entityplayer.openContainer).getLowerChestInventory();
    
                        if (iinventory == teChest || iinventory instanceof InventoryLargeChest && ((InventoryLargeChest)iinventory).isPartOfLargeChest(teChest))
                        {
                            ++teChest.numPlayersUsing;
                        }
                    }
                }
            }
    
            teChest.prevLidAngle = teChest.lidAngle;
            f = 0.1F;
            double d2;
    
            if (teChest.numPlayersUsing > 0 && teChest.lidAngle == 0.0F && teChest.adjacentChestZNeg == null && teChest.adjacentChestXNeg == null)
            {
                double d1 = (double)teChest.xCoord + 0.5D;
                d2 = (double)teChest.zCoord + 0.5D;
    
                if (teChest.adjacentChestZPos != null)
                {
                    d2 += 0.5D;
                }
    
                if (teChest.adjacentChestXPos != null)
                {
                    d1 += 0.5D;
                }
    
                teChest.getWorldObj().playSoundEffect(d1, (double)teChest.yCoord + 0.5D, d2, "random.chestopen", 0.5F, teChest.getWorldObj().rand.nextFloat() * 0.1F + 0.9F);
            }
    
            if (teChest.numPlayersUsing == 0 && teChest.lidAngle > 0.0F || teChest.numPlayersUsing > 0 && teChest.lidAngle < 1.0F)
            {
                float f1 = teChest.lidAngle;
    
                if (teChest.numPlayersUsing > 0)
                {
                    teChest.lidAngle += f;
                }
                else
                {
                    teChest.lidAngle -= f;
                }
    
                if (teChest.lidAngle > 1.0F)
                {
                    teChest.lidAngle = 1.0F;
                }
    
                float f2 = 0.5F;
    
                if (teChest.lidAngle < f2 && f1 >= f2 && teChest.adjacentChestZNeg == null && teChest.adjacentChestXNeg == null)
                {
                    d2 = (double)teChest.xCoord + 0.5D;
                    double d0 = (double)teChest.zCoord + 0.5D;
    
                    if (teChest.adjacentChestZPos != null)
                    {
                        d0 += 0.5D;
                    }
    
                    if (teChest.adjacentChestXPos != null)
                    {
                        d2 += 0.5D;
                    }
    
                    teChest.getWorldObj().playSoundEffect(d2, (double)teChest.yCoord + 0.5D, d0, "random.chestclosed", 0.5F, teChest.getWorldObj().rand.nextFloat() * 0.1F + 0.9F);
                }
    
                if (teChest.lidAngle < 0.0F)
                {
                    teChest.lidAngle = 0.0F;
                }
            }
        }
    }
    
    

    Voilà, une fois que ce problème sera résolu, j'irai parcourir les itemstack contenus dans la te, et affecter des dates de pourriture concernant les viandes, à travers les NBTTag 😃
    Alors qu'au cas où j'ai un plan b, qui consisterait à passer par l'event WorldTickEvent et de parcourir la liste des te dans le monde, mais je préfère me servir des coremod, ça m'entraîne davantage 😄

    Merci d'avance !

    EDIT = J'attends une réponse jusqu'au week-end prochain, sinon j'opterai pour le plan B !</localvariablenode></string,>


  • Modérateurs

    L'instruction INVOKESTATIC ne peut être utilisée qu'avec des méthodes statiques.

    Il faut que tu mettes ta méthode Patch::updateEntityPatch(TileEntityChest) en statique.