提供: Minecraft Modding Wiki
移動先: 案内検索

警告: ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。ログインまたはアカウントを作成すれば、あなたの編集はその利用者名とともに表示されるほか、その他の利点もあります。

この編集を取り消せます。 下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 編集中の文章
6行目: 6行目:
  
 
===Coremodとは===
 
===Coremodとは===
通常 Minecraft.jar 内に上書きが必要な変更を施すような 前提MOD(API系)や既存書き換え系MODなどを、coremods フォルダに入れるだけでインストールできるようにする、FMLの機能です。
+
通常 Minecraft.jar 内に上書きが必要な変更を施すような mod(前提Modなど等)を、coremods フォルダに入れるだけでインストールできるようにする機能です。
  
 
ただし、既存書き換えのMODをそのまま coremod にすることは出来ません。
 
ただし、既存書き換えのMODをそのまま coremod にすることは出来ません。
12行目: 12行目:
  
 
===仕組み===
 
===仕組み===
FMLでは、Minecraftが起動した直後に処理を割り込ませ、現在のスレッドの ContextClassLoader に、net.minecraft.launchwrapper.LaunchClassLoader を設定しています。このクラスローダーでは、クラスロード時にバイトコードのクラスを編集するポイントが設けられており、Minecraftが実行中に読み込む殆どのクラス(※)を、ロード時に動的に改変する事が可能となっています。
+
FMLでは、Java のデフォルトクラスローダーではなく、独自のクラスローダーを用いてクラスを読み込むように、Minecraftを改変します。通常コンパイル済みのClassファイルの内容は変更できませんが、クラスローダーを差し替えることで、クラスが読まれる直前に、そのバイトコードに対して変更を加えるポイントの追加を実現しています。
  
※全てのクラスの変換が行えるわけではありません。IClassTransformer を通さないよう、変換対象から除外登録されている一部のパッケージ以下のクラスは、本来のクラスローダーであるシステムクラスローダーによりロードされてしまうため、動的な変換処理を行えません。またプラグイン側で TransformerExclusions アノテーションを用い除外設定されているパッケージやクラスも変換対象外となります。
+
この機能を利用するのが、このチュートリアルで解説する Coremod となります。
  
詳細は RelaunchClassLoader の実装を確認するか、実際に試してみて判断ください。また、クラスローダーそのものの仕組みについては、Web検索で多くの情報を得る事が出来ますのでここでは割愛します。
+
Coremod を用いる事で、Minecraftの実行中に、初めてクラスがロードされた際に、クラスのバイトコードを置換、または部分的に書き換える事ができるようになります。動的に書き換えるため、Minecraft.jar 内(※Coremod部分以外のFMLコードも含む)および、mods 内の zip 等のClassファイルを、直接上書きして変更することなく改変することを可能とします。
  
当チュートリアルでは、主にこのクラス変換機能の実装方法を解説します。
+
====動的なクラス書き換えって、重くないの?====
 +
クラスのロードは、基本的に最初にクラスの参照が要求された際に、1回だけ行われます。
  
この機能を用いることで、Minecraftの実行中に、初めてクラスがロードされた際に、クラスのバイトコードを置換、または部分的に書き換える事ができるようになります。動的に書き換えるため、Minecraft.jar 内(※Coremod部分以外のFMLコードも含む)および、mods 内の zip 等のClassファイルを、直接上書きして変更することなく改変することを可能とします。
+
一度ロードされたクラスは、通常どおりコンパイルされたclassファイルとなんら違いはありません。
 
 
====動的なクラス書き換えって、重くないの?====
 
クラスのロードは、基本的に最初にクラスの参照が要求された際に、1回だけ行われます。一度ロードされたクラスは、通常どおりコンパイルされたclassファイルとなんら違いはありません。
 
  
 
つまり、施した改変内容以上のパフォーマンスへの影響は、ほぼ無いと考えて差し支えありません。
 
つまり、施した改変内容以上のパフォーマンスへの影響は、ほぼ無いと考えて差し支えありません。
58行目: 56行目:
  
 
# 任意の名前のクラス(ここではTutorialCorePlugin)を作成します。
 
# 任意の名前のクラス(ここではTutorialCorePlugin)を作成します。
# IFMLLoadingPlugin、<strike>IFMLCallHook</strike> を実装します。
+
# IFMLLoadingPlugin、IFMLCallHook を実装します。
 
 
net.minecraftforge.fml.relauncher.IFMLLoadingPlugin は、このクラス自体に実装する必要はありません。
 
getSetupClass() メソッドで返される名前のクラスが、IFMLCallHookを実装している必要があります。
 
なお、本チュートリアルでは、コールフックを使用していないため、getSetupClass()メソッドの戻り値は null としています。 
 
  
 
<source lang="java">
 
<source lang="java">
75行目: 69行目:
 
import java.util.Map;
 
import java.util.Map;
  
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
+
import cpw.mods.fml.relauncher.IFMLCallHook;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions;
+
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
 +
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions;
  
// TransformerExclusions: Transformerから除外するクラス名を設定するためのアノテーション
+
// TransformerExclusions.value: coremods でロードする際に参照されるため、パッケージ名と一致させてください。
//  
+
// IFMLLoadingPlugin: Coremods の基礎インタフェース
// 値は文字列の配列で、複数指定も可能です。
+
// IFMLCallHook: Coremods 内で、coremod 自身のパス等を取得する等の際に必要となります。
//  指定した文字列と前方一致するクラス名は、後述のクラスの動的な変換処理から除外されます。
 
//  例えば、自身のクラスが変換されないように、自身のパッケージ以下を除外指定する、などが出来ます。
 
//  必須ではありません。必要に応じて設定してください。
 
//  本チュートリアルでは、参考として自身のパッケージを変換処理から除外しています。
 
//
 
// IFMLLoadingPlugin: Coremods としてロードするために必要なインタフェース
 
  
@TransformerExclusions({"tutorial.asm"})
+
@TransformerExclusions(value={"tutorial.asm"})
public class TutorialCorePlugin implements IFMLLoadingPlugin
+
public class TutorialCorePlugin implements IFMLLoadingPlugin, IFMLCallHook
 
{
 
{
     // coremod の jar ファイルのパス抽象表現を保持します。
+
     // coremod 自身のファイルパスの保存用
    // Transformer 以外から呼ばれることは考慮しないため、デフォルトのアクセス指定子としています。
+
     public static File location;
 
+
      
     static File location;
+
     // 今回は使用しません
 
 
     // このプラグインが動作するために必要となるライブラリセットのクラス名の配列です。
 
    // 本チュートリアルでは使用しないため説明は割愛します。
 
     // インターフェイスの javadocや、FMLCorePlugin クラスの実装を参照してみてください。
 
 
 
 
     @Override
 
     @Override
 
     public String[] getLibraryRequestClass()
 
     public String[] getLibraryRequestClass()
106行目: 90行目:
 
     }
 
     }
  
     // Class の改変機能を実装したクラスの完全修飾名の配列を返します。
+
     // Class の改変機能を実装したクラスの完全修飾名を返します。
    // 本チュートリアルの変換処理クラスは TutorialTransformer のみなので、一つだけを配列に詰め返却しています。
 
 
 
 
     @Override
 
     @Override
 
     public String[] getASMTransformerClass()
 
     public String[] getASMTransformerClass()
115行目: 97行目:
 
     }
 
     }
  
     // coremod の名前やバージョン情報を格納しているクラスの完全修飾名を返します。
+
     // coremod の名前やバージョン情報の格納クラスの完全修飾名を返します。
 
 
 
     @Override
 
     @Override
 
     public String getModContainerClass()
 
     public String getModContainerClass()
123行目: 104行目:
 
     }
 
     }
  
     // IFMLCallHook を実装しているクラス名を返す必要があります。
+
     // coremod 読み込みの基点クラスの完全修飾名を返します。
    // 本チュートリアルでは、コールフックを用いないため、こちらの説明も割愛します。
 
 
 
 
     @Override
 
     @Override
 
     public String getSetupClass()
 
     public String getSetupClass()
 
     {
 
     {
         return null;
+
         return "tutorial.asm.TutorialCorePlugin";
 
     }
 
     }
  
     // IFMLLoadingPlugin のメソッドです。(IFMLCallHook にも同じシグネチャーのメソッドがありますが、違います)
+
     // IFMLCallHook のメソッドです。
     // 今回は coremod 自身の jar ファイルパスを取得しています。これは後述のトランスフォーマークラスで、
+
     // 今回は coremod 自身の jar ファイルパスを取得します。
    // jarから置換用クラスを取得しているためで、そのような処理を行わないのであれば何も実装しなくても構いません。
 
    //
 
    // なお、IFMLLoadingPlugin のメソッドとして呼ばれた際は、"mcLocation"、"coremodList"、"coremodLocation" の3つ、
 
    // IFMLCallHook のメソッドとして呼ばれた際は、"classLoader" がマップに設定されています。(FML#511現在)
 
    //
 
    // 渡されるマップの中身は、net.minecraftforge.fml.relauncher.RelaunchLibraryManager の実装からも確認する事が出来ます。
 
 
 
 
     @Override
 
     @Override
 
     public void injectData(Map<String, Object> data)
 
     public void injectData(Map<String, Object> data)
148行目: 120行目:
 
             location = (File) data.get("coremodLocation");
 
             location = (File) data.get("coremodLocation");
 
         }
 
         }
 +
    }
 +
 +
    @Override
 +
    public Void call() throws Exception
 +
    {
 +
        return null;
 
     }
 
     }
 
  }
 
  }
172行目: 150行目:
 
import com.google.common.eventbus.Subscribe;
 
import com.google.common.eventbus.Subscribe;
  
import net.minecraftforge.fml.common.DummyModContainer;
+
import cpw.mods.fml.common.DummyModContainer;
import net.minecraftforge.fml.common.LoadController;
+
import cpw.mods.fml.common.LoadController;
import net.minecraftforge.fml.common.ModMetadata;
+
import cpw.mods.fml.common.ModMetadata;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
+
import cpw.mods.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
+
import cpw.mods.fml.common.versioning.ArtifactVersion;
  
 
// 必ずしも DummyModContainer を継承している必要はありません。
 
// 必ずしも DummyModContainer を継承している必要はありません。
// net.minecraftforge.fml.common.ModContainer さえ実装していれば、どんなクラスでも構いません。
+
// cpw.mods.fml.common.ModContainer さえ実装していれば、どんなクラスでも構いません。
  
 
public class TutorialModContainer extends DummyModContainer
 
public class TutorialModContainer extends DummyModContainer
197行目: 175行目:
 
         meta.url        = "";
 
         meta.url        = "";
 
         meta.credits    = "";
 
         meta.credits    = "";
        this.setEnabledState(true);
 
    }
 
    @Override
 
    public boolean registerBus(EventBus bus, LoadController controller)
 
    {
 
bus.register(this);
 
return true;
 
 
     }
 
     }
 
}
 
}
222行目: 193行目:
 
import java.util.zip.ZipFile;
 
import java.util.zip.ZipFile;
  
 +
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.MethodInsnNode;
 +
import org.objectweb.asm.tree.MethodNode;
 +
import org.objectweb.asm.tree.VarInsnNode;
  
import net.minecraftforge.fml.relauncher.FMLRelauncher;
+
import cpw.mods.fml.relauncher.FMLRelauncher;
import net.minecraftforge.fml.relauncher.IClassTransformer;
+
import cpw.mods.fml.relauncher.IClassTransformer;
  
public class TutorialTransformer implements IClassTransformer
+
// Opcodes : インプリメントすると、ASMによるバイトコード定数にアクセスするのに便利です。
 +
// 必須ではありません。不用な場合は implements から削除してください。
 +
 
 +
public class TutorialTransformer implements IClassTransformer, Opcodes
 
{
 
{
 
     // 改変対象のクラスの完全修飾名です。
 
     // 改変対象のクラスの完全修飾名です。
233行目: 215行目:
  
 
     // クラスがロードされる際に呼び出されるメソッドです。
 
     // クラスがロードされる際に呼び出されるメソッドです。
     public byte[] transform(final String name, final String transformedName, byte[] baseClass) {
+
    @Override
 
+
     public byte[] transform(String name, byte[] bytes)
 +
    {
 
         // FMLRelauncher.side() : Client/Server どちらか一方のを対象とする場合や、
 
         // FMLRelauncher.side() : Client/Server どちらか一方のを対象とする場合や、
 
         // 一つのMODで Client/Sever 両方に対応したMODで、この値を判定して処理を変える事ができます。
 
         // 一つのMODで Client/Sever 両方に対応したMODで、この値を判定して処理を変える事ができます。
241行目: 224行目:
  
 
         // name : 現在ロードされようとしているクラス名が格納されています。
 
         // name : 現在ロードされようとしているクラス名が格納されています。
         if (/*!FMLRelauncher.side().equals("CLIENT") || */!transformedName.equals(TARGET_CLASS_NAME))
+
         if (!FMLRelauncher.side().equals("CLIENT") || !name.equals(TARGET_CLASS_NAME))
 
         {
 
         {
 
             // 処理対象外なので何もしない
 
             // 処理対象外なので何もしない
249行目: 232行目:
 
         try
 
         try
 
         {
 
         {
             // bytesに元のクラスの生情報が入っているので、ASMなどで処理し返す。
+
             // --------------------------------------------------------------
             return doSomething(bytes);
+
             // クラスファイル丸ごと差し替える場合
 +
            // --------------------------------------------------------------
 +
            // return replaceClass(bytes);
 +
 
 +
            // --------------------------------------------------------------
 +
            // ASMを使用し、既存のクラスファイルに改変を施す場合。
 +
            // --------------------------------------------------------------
 +
            // return hookDoRenderLivingMethod(bytes);
 +
 
 
         }
 
         }
 
         catch (Exception e)
 
         catch (Exception e)
256行目: 247行目:
 
             throw new RuntimeException("failed : TutorialTransformer loading", e);
 
             throw new RuntimeException("failed : TutorialTransformer loading", e);
 
         }
 
         }
 +
    }
 +
 +
    // 下記の想定で実装されています。
 +
    // 対象クラスの bytes を ModifiedTargetClass.class ファイルに置き換える
 +
    private byte[] replaceClass(byte[] bytes) throws IOException
 +
    {
 +
        ZipFile zf = null;
 +
        InputStream zi = null;
 +
 +
        try
 +
        {
 +
            zf = new ZipFile(TutorialCorePlugin.location);
 +
 +
            // 差し替え後のファイルです。coremodのjar内のパスを指定します。
 +
            ZipEntry ze = zf.getEntry("ModifiedTargetClass.class");
 +
 +
            if (ze != null)
 +
            {
 +
                zi = zf.getInputStream(ze);
 +
                bytes = new byte[(int) ze.getSize()];
 +
                zi.read(bytes);
 +
            }
 +
 +
            return bytes;
 +
        }
 +
        finally
 +
        {
 +
            if (zi != null)
 +
            {
 +
                zi.close();
 +
            }
 +
 +
            if (zf != null)
 +
            {
 +
                zf.close();
 +
            }
 +
        }
 +
    }
 +
 +
    // 下記の想定で実装されています。
 +
    // EntityLiving.class の doRenderLiving の先頭に
 +
    // tutorial/test.class の passTestRender(EntityLiving, double, double, double)メソッドの呼び出しを追加する。
 +
    private byte[] hookDoRenderLivingMethod(byte[] bytes)
 +
    {
 +
        // ASMで、bytesに格納されたクラスファイルを解析します。
 +
        ClassNode cnode = new ClassNode();
 +
        ClassReader reader = new ClassReader(bytes);
 +
        reader.accept(cnode, 0);
 +
 +
        // 改変対象のメソッド名です
 +
        String targetMethodName = "doRenderLiving";
 +
 +
        // 改変対象メソッドの戻り値型および、引数型をあらわします
 +
        String targetMethoddesc = "(Lnet/minecraft/entity/EntityLiving;DDDFF)V";
 +
 +
        // 対象のメソッドを検索取得します。
 +
        MethodNode mnode = null;
 +
        for (MethodNode curMnode : (List<MethodNode>) cnode.methods)
 +
        {
 +
            if (targetMethodName.equals(curMnode.name) && targetMethoddesc.equals(curMnode.desc))
 +
            {
 +
                mnode = curMnode;
 +
                break;
 +
            }
 +
        }
 +
 +
        if (mnode != null)
 +
        {
 +
            InsnList overrideList = new InsnList();
 +
 +
            // メソッドコールを、バイトコードであらわした例です。
 +
            overrideList.add(new VarInsnNode(ALOAD, 1));
 +
            overrideList.add(new VarInsnNode(DLOAD, 2));
 +
            overrideList.add(new VarInsnNode(DLOAD, 4));
 +
            overrideList.add(new VarInsnNode(DLOAD, 6));
 +
            overrideList
 +
                    .add(new MethodInsnNode(INVOKESTATIC, "tutorial/test", "passTestRender", "(LEntityLiving;DDD)V"));
 +
 +
            // mnode.instructions.get(1)で、対象のメソッドの先頭を取得
 +
            // mnode.instructions.insertで、指定した位置にバイトコードを挿入します。
 +
            mnode.instructions.insert(mnode.instructions.get(1), overrideList);
 +
 +
            // 改変したクラスファイルをバイト列に書き出します
 +
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
 +
            cnode.accept(cw);
 +
            bytes = cw.toByteArray();
 +
        }
 +
 +
        return bytes;
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
 
※1 詳しくは[http://asm.ow2.org/doc/faq.html#Q7 ここ]を参照
 
  
 
===META-INF/MANIFEST.MFファイルの作成===
 
===META-INF/MANIFEST.MFファイルの作成===
  
 
# META-INF/MANIFEST.MFファイルを作成します。
 
# META-INF/MANIFEST.MFファイルを作成します。
# FMLCorePlugin節にTutorialCorePlugin の完全修飾クラスの名(パッケージ名を含む名前)を指定します。
+
# TutorialCorePlugin の完全修飾クラスの名(パッケージ名を含む名前)を指定します。
 +
 
 
<source lang="java">
 
<source lang="java">
 
Manifest-Version: 1.0
 
Manifest-Version: 1.0
 
FMLCorePlugin: tutorial.asm.TutorialCorePlugin
 
FMLCorePlugin: tutorial.asm.TutorialCorePlugin
</source>
 
 
上記の場合、jarに通常のFML(@Mod)クラスやmcmod.infoを含めてもロードされません。
 
通常のFMLクラスを含めたい場合はFMLCorePluginContainsFMLMod節を追加します。値はなんでもいいです。
 
この場合、FMLでロードしたいクラス(@Modが付加されたクラス)はTransformerクラス(このチュートリアルではasm)等とpackageが異なる必要があります。
 
<source lang="java">
 
FMLCorePluginContainsFMLMod: *
 
 
</source>
 
</source>
  
291行目: 363行目:
 
**** TutorialModContainer.class
 
**** TutorialModContainer.class
 
**** TutorialTransformer.class
 
**** TutorialTransformer.class
** ModifiedTargetClass.class (丸ごと差し替える場合に用いるクラス(難読化済み))
+
** ModifiedTargetClass.class (丸ごと差し替える場合に用いるクラス)
 
    
 
    
 
通常の mod のように、zip で圧縮し、拡張子を .zip から .jar に変更すれば完成です。
 
通常の mod のように、zip で圧縮し、拡張子を .zip から .jar に変更すれば完成です。
  
 
※jarコマンドを用いて作成することも可能ですが、今回は割愛します。
 
※jarコマンドを用いて作成することも可能ですが、今回は割愛します。
 
※1.8から
 
*ワークスペース(Gradlew.batがあるフォルダ)
 
** src
 
*** main
 
**** java
 
***** tutorial
 
******* asm
 
******** TutorialCorePlugin.class
 
******** TutorialModContainer.class
 
******** TutorialTransformer.class
 
**** resources
 
***** ModifiedTargetClass.class (丸ごと差し替える場合に用いるクラス(難読化済み))
 
ForgeGradleが勝手に1.7のようにやってくれます。
 
  
 
==テスト、デバッグ方法について==
 
==テスト、デバッグ方法について==
<del>coremod 形式の mod は、Eclipse でのデバッグで動作させるのは手間がかかります。</del>
+
coremod 形式の mod は、Eclipse でのデバッグで動作させるのは手間がかかります。
<del>機能の実装部分については、書き換え元のクラスを、直接改変して試したほうが簡単でしょう。</del>
+
機能の実装部分については、書き換え元のクラスを、直接改変して試したほうが簡単でしょう。
  
<del>coremodとしての動作確認は、実際の動作環境へ放り込んで行ってください。</del>
+
coremodとしての動作確認は、実際の動作環境へ放り込んで行ってください。
 
 
通常 coremods フォルダーにある場合のみ coremod として読み込まれるため、作成した coremod にただクラスパスを通すだけでは読み込まれません。しかし、FMLには環境変数 fml.coreMods.load を用いた coremod の読み込み機能が用意されています。これを用いることで、任意のプラグインを coremod として読み込ませる事が可能です。
 
 
 
なお、環境変数に指定するのは割と不便なので、JVM起動時の引数としてVMに環境変数を追加して指定するほうが便利です。
 
 
 
やり方はとても簡単で、デバッグ実行する際のJVMの引数に、以下を追加します。
 
 
 
<source lang="dos">-Dfml.coreMods.load=完全修飾クラス名CSV</source>
 
※-Dオプションについての詳細は検索してください。
 
 
 
引数にはカンマ区切りで複数のクラス名を指定可能です。なお、両端トリムはされないため余計なスペースなどを含まないよう注意してください。(Ex: =com.example.mod.PluginAAA, com.example.mod.pluginBBB ; カンマの後にスペースを挟んでしまっている点が間違い。)
 
 
 
またこの際、coremod のクラスのある場所へのクラスパスを追加するのも忘れないでください。
 
 
 
* Eclipseでの設定手順(MCP付属のワークスペースを用いており、既にClientのデバッグ構成があるものとします)
 
# デバッグの構成を開きます。
 
# Clientのデバッグ構成を選択し、引数タブを選択します。
 
# VM引数に、上記の引数を追加します。
 
# 必要に応じてプラグインへのクラスパスを追加します。
 
 
 
サーバーへのcoremod追加も手順は同じです。
 
 
 
正しくcoremodとして読み込まれた場合、コンソールに
 
<source lang="java">yyyy-MM-dd hh:mm:ss [INFO] [ForgeModLoader] Found a command line coremod : プラグインの完全修飾名</source>
 
が出力されます。(標準エラーを表示している場合)
 
  
 
==難読化への対抗手段の解説==
 
==難読化への対抗手段の解説==
===Minecraft1.5.0以降===
 
;改善された改変対象の比較検索方法
 
:IClassTransformer.transformメソッドに引数"transformedName"が追加されました。
 
:これは、易読化されたクラス名が渡されるため、開発時と同様のクラス名で比較することができるようになりました。
 
  
:また、Method名やMethodDescについても同様に、易読化する手段が提供されています。
+
===準備===
:Method名の場合
+
# 改変したいClassを書き換える。
:FMLDeobfuscatingRemapper.INSTANCE.mapMethodName(class名,Method名,MethodDesc)
+
# recompile > reobfuscate し、難読化後のクラスファイルを作成する。
:MethodDescの場合
 
:FMLDeobfuscatingRemapper.INSTANCE.mapMethodDesc(MethodDesc)
 
:※渡すのは、どちらも難読化されているもの 逆に難読化する方法については未調査です。
 
  
;ASMライブラリを用いたクラスの部分改変への改善
+
===クラスを丸ごと差し替える場合===
:開発環境と同様とまでは行きませんが、deobf済みのclass名、method名、methodDescを用いて追加しても動作するようになりました。
+
reobfに出力されたクラスファイル名を見てTutorialTransformerを適宜書き換えましょう。
:このため、Eclipse 等の ByteCodeOutline プラグインなどで得たコードをあるていどならば、ほぼそのまま利用できるようになりました。
 
  
===Minecraft1.4.7まで===
+
※置き変えてしまうので、同一のクラスを置き買えるものとは競合してしまいます。
;準備
 
: 改変したいClassを書き換える。
 
: recompile > reobfuscate し、難読化後のクラスファイルを作成する。
 
  
;クラスを丸ごと差し替える場合
+
===ASMライブラリを用いたクラスの部分改変を行う場合===
:reobfに出力されたクラスファイル名を見てTutorialTransformerを適宜書き換えましょう。
+
reobf/ に出力されたクラスファイルと、改変前のクラスファイルを、JBVD等の適当な ByteCodeViewer で開き、2つを比較して改変された部分を特定します。
 +
それを元にASMのコードに置き変え、TutorialTransformer を適宜書き換えましょう。
  
:※置き変えてしまうので、同一のクラスを置き買えるものとは競合してしまいます。
+
サンプルのようなメソッドのフック処理程度であれば、Eclipse 等の ByteCodeOutline プラグインなどで、改変部分のコードを直接ASM形式のコードに変換し、難読化される部分を書きかえる程度の修正で、そのまま使える場合もあります。
  
;ASMライブラリを用いたクラスの部分改変を行う場合
+
※ ByteCode操作の場合、同一クラスにに複数の改変を施すことができるため、競合させずにそれぞれの改変を施すこともできます。
:reobf/ に出力されたクラスファイルと、改変前のクラスファイルを、JBVD等の適当な ByteCodeViewer で開き、2つを比較して改変された部分を特定します。
 
:それを元にASMのコードに置き変え、TutorialTransformer を適宜書き換えましょう。
 
 
 
:サンプルのようなメソッドのフック処理程度であれば、Eclipse 等の ByteCodeOutline プラグインなどで、改変部分のコードを直接ASM形式のコードに変換し、:難読化される部分を書きかえる程度の修正で、そのまま使える場合もあります。
 
 
 
:※ ByteCode操作の場合、同一クラスにに複数の改変を施すことができるため、競合させずにそれぞれの改変を施すこともできます。
 
  
 
==雑記==
 
==雑記==
381行目: 398行目:
 
別な処理に置き変えるなど、Javaでできることはほぼなんでも出来ますが<br />
 
別な処理に置き変えるなど、Javaでできることはほぼなんでも出来ますが<br />
 
ASMライブラリの使用方法を別途調べる必要があります。(記述時点で、日本語解説はあまり多くありません。)
 
ASMライブラリの使用方法を別途調べる必要があります。(記述時点で、日本語解説はあまり多くありません。)
大まかな解説についてはこのWikiにも解説があります(→[[ASM利用]])。
 
 
ASMライブラリの既知の不具合
 
・関数外のstaticフィールドの初期化を行うコードがあるとClassWriterがNullPointerExceptionを引き起こします。
 
 
こめ
 
  
 
<comments />
 
<comments />
----
 
* MANIFEST.MF 内の FMLCorePluginContainsFMLMod について追記しました。 --[[特別:投稿記録/118.22.179.98|118.22.179.98]] 2014年2月7日 (金) 08:58 (JST)
 
----
 
* 1.6 では FMLRelauncher.side() &gt;&gt; FMLLaunchHandler.side() と変更されているようです。パッケージもかなり移動しているようなので追記が必要かも知れません。 --[[特別:投稿記録/122.251.192.117|122.251.192.117]] 2013年7月8日 (月) 00:44 (JST)
 
----
 
* とてもいいチュートリアルなので、体裁などを修正させて頂きました。問題があるようでしたら差し戻してください。 --[[特別:投稿記録/219.125.180.254|219.125.180.254]] 2013年1月7日 (月) 22:24 (JST)
 
 
[[Category:その他]]
 
[[Category:その他]]

Minecraft Modding Wikiへの投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細はMinecraft Modding Wiki:著作権を参照)。 著作権保護されている作品は、許諾なしに投稿しないでください!

このページを編集するには、下記の確認用の質問に回答してください (詳細):

取り消し 編集の仕方 (新しいウィンドウで開きます)

このページで使用されているテンプレート: