[JAVA] Wie schreibe ich einen Core Mod in Minecraft Forge 1.15.2

Ich werde kurz erklären, wie man einen Core-Mod in der Minecraft Forge 1.15.2-Umgebung schreibt. Was ist ASM? Was ist ein Bytecode? Ich werde es hier nicht erklären. Entschuldigung. Ich werde Javascript auch nicht erklären. Entschuldigung.

Wenn Sie jedoch ein Modder sind, der Core-Mods verwendet, können Sie mit einem Gefühl schreiben.

Forge 1.13 oder höher wird wahrscheinlich auf die gleiche Weise funktionieren, aber ich habe es nicht überprüft.

Ich habe sehr viel auf diese Seite und den Beispielcode verwiesen. Ich bin meinen Vorgängern zutiefst dankbar. 99.99 - Coremod

Was ist Coremod? Was ist ASM?

Immer noch einfach. "coremod" ist einer der von Forge bereitgestellten Mechanismen, mit denen Sie den Java-Code von Minecraft selbst neu schreiben können (es gibt einige Missverständnisse). Die dafür verwendete Bibliothek ist ASM. Außerdem ist der Code selbst nicht der menschliche Code, den Sie normalerweise schreiben, sondern der kompilierte Bytecode. Wenn Sie ASM schreiben, können Sie auf jeden Fall alles tun. Der Wettbewerb mit anderen Mods kann jedoch zunehmen.

Dieses PDF wird dringend empfohlen (aber lang), um ASM zu studieren. ASM 4.0 A Java bytecode engineering library

Überprüfungsumgebung

Minecraft: 1.15.2 Minecraft Forge: 1.15.2-31.2.0

Änderungen vor 1.13

Früher wurde coremod in Java-Code geschrieben, ab 1.13 wird es jedoch in ** Javascript ** geschrieben. Es ist in Ordnung zu glauben, dass der ASM selbst (wahrscheinlich) genauso geschrieben ist wie zuvor.

Dateiorganisation

Es sind mindestens zwei Dateien erforderlich.

coremods.json Platziere es direkt unter ** main / java / resources / META-INF / folder **. Der Dateiname sollte wahrscheinlich "coremods.json" sein.

Der Inhalt ist wie folgt.

coremods.js


{
  "TestMod Transformer": "testmod-transformer.js"
}

Der Schlüssel im Array ("Test Mod Transformer") ist wahrscheinlich alles. Ich denke, es ist egal, was der Fall ist. Schreiben Sie für den Wert den Speicherort der js-Datei, die Sie schreiben möchten.

Wenn Sie nur den Dateinamen wie oben schreiben, geben Sie die Datei in ** main / java / resources / direkt unter ** an. Ich habe nicht versucht, ob der absolute Pfad oder der relative Pfad funktioniert, aber ich habe das Gefühl, dass der relative Pfad zu funktionieren scheint.

coremods.js


{
  "TestMod Transformer": "testmod-transformer.js",
  "TestMod Transformer2": "testmod-transformer2.js"
}

Sie können auch mehrere Javascript-Dateien gleichzeitig angeben.

Javascript-Datei zum Schreiben von ASM

Der Klarheit halber setzen wir es direkt unter main / java / resources /. Wenn Sie den Speicherort ändern, ändern Sie bitte den Wert des Inhalts von coremods.json oben. Das js, das tatsächlich ASM schreibt, sieht wie folgt aus.

testmod-transformer.js


function initializeCoreMod() {
    return {
        'coremodmethod': {
            'target': {
                'type': 'METHOD',
                'class': 'your.target.class',
                'methodName': 'targetMethodName',
                'methodDesc': 'targetMethodDescriptor'
            },
            'transformer': function(method) {
                //Schreiben Sie hier die ASM-Verarbeitung.
                return method;
            }
        }
    }
}

In die js-Datei müssen Sie eine Funktion mit dem Namen "initializeCoreMod ()" schreiben, die json zurückgibt. Es ist in Ordnung, wenn Sie oben kopieren und einfügen. Wenn Sie im obigen Abschnitt "ASM-Verarbeitung hier schreiben" verschiedene Dinge schreiben, ist dies abgeschlossen.

Danke für deine harte Arbeit.               Dann kommst du normalerweise hierher? Es scheint gesagt zu werden, also werde ich ein bisschen mehr schreiben. Wenn Sie das oben Genannte verstehen, können Sie den Rest erledigen.

Transformatortyp

Sie können drei Arten von Zielen angeben: "FELD", "METHODE" und "KLASSE". Es stört Felder, Methoden bzw. Klassen. Mit anderen Worten, Sie können die folgenden drei Möglichkeiten schreiben.

testmod-transformer.js


function initializeCoreMod() {
    return {
        'coremodmethod': {
            'target': {
                'type': 'FIELD',
                'class': 'your.target.class',
                'fieldName': 'targetFieldName'
            },
            'transformer': function(field) {
                //Schreiben Sie hier die ASM-Verarbeitung.
                return field;
            }
        }
    }
}

testmod-transformer.js


function initializeCoreMod() {
    return {
        'coremodmethod': {
            'target': {
                'type': 'METHOD',
                'class': 'your.target.class',
                'methodName': 'targetMethodName',
                'methodDesc': 'targetMethodDescriptor'
            },
            'transformer': function(method) {
                //Schreiben Sie hier die ASM-Verarbeitung.
                return method;
            }
        }
    }
}

testmod-transformer.js


function initializeCoreMod() {
    return {
        'coremodmethod': {
            'target': {
                'type': 'CLASS',
                'class': 'your.target.class'
            },
            'transformer': function(class) {
                //Schreiben Sie hier die ASM-Verarbeitung.
                return class;
            }
        }
    }
}

Die erforderlichen Werte in "Ziel" sind wie oben. Aus dem persönlichen Grund, dass das Feld von Java aus den Reflection Helper stören kann und es oft genug ausreicht, um die Methode zu stören, auch wenn es die Klasse selbst nicht stört, werde ich FIELD und CLASS unten ändern. Wird nicht berühren. Jemand bitte.

Wie man konkret schreibt

function initializeCoreMod() {
    var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
    var Opcodes = Java.type('org.objectweb.asm.Opcodes');
    var InsnList = Java.type("org.objectweb.asm.tree.InsnList");
    var InsnNode = Java.type("org.objectweb.asm.tree.InsnNode");

    var mappedMethodName = ASMAPI.mapMethod("target_srg_func_name");

    return {
        'coremodmethod': {
            'target': {
                'type': 'METHOD',
                'class': 'your.target.class',
                'methodName': mappedMethodName,
                'methodDesc':'targetMethodDescriptor'
            },
            'transformer': function(method) {
                print("Enter transformer.");

                var arrayLength = method.instructions.size();
                var target_instruction = null;

                //search the target instruction from the entire instructions.
                for(var i = 0; i < arrayLength; ++i) {
                    var instruction = method.instructions.get(i);
                    if(instruction.name === "targetMethodName") {
                        target_instruction = instruction;
                        break;
                    }
                }

                if(target_instruction == null) {
                    print("Failed to detect target.");
                    return method;
                }

                var toInject = new InsnList();
//              toInject.add(new InsnNode(Opcodes.POP));
                toInject.add(new InsnNode(Opcodes.RETURN));

                // Inject new instructions just after the target.
                method.instructions.insert(target_instruction, toInject);

                return method;
            }
        }
    }
}

Ich denke, die Aussichten haben sich erheblich verbessert. Ich werde in der Reihenfolge erklären.

Java-Klassenaufruf

    var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
    var Opcodes = Java.type('org.objectweb.asm.Opcodes');
    var InsnList = Java.type("org.objectweb.asm.tree.InsnList");
    var InsnNode = Java.type("org.objectweb.asm.tree.InsnNode");

Ich weiß nicht, was für eine Art von Magie es ist, aber wenn Sie den üblichen Namen der Importzielklasse in das Argument "Java.type ()" einfügen, können Sie es so verwenden, als wäre es Java (Erklärung Verschiedenes). Wenn Opcodes kommen, habe ich das Gefühl, dass es bereits 100 Leute gibt. Aber ich habe Probleme mit der Code-Vervollständigung, die nicht funktioniert. Wenn jemand weiß, wie man es ergänzt, lass es mich wissen. Ich bin sicher, dass ASMAPI und Opcodes alle Änderungen verwenden werden, aber ich denke, Sie bevorzugen es, andere Klassen aufzurufen. Das aktuelle Coremod verwendet die sogenannte Tree-API. Überprüfen Sie daher dort separat, welche Klassen verfügbar sind.

Festlegen von Störmethoden

    var mappedMethodName = ASMAPI.mapMethod("target_srg_func_name");

    return {
        'coremodmethod': {
            'target': {
                'type': 'METHOD',
                'class': 'your.target.class',
                'methodName': mappedMethodName,
                'methodDesc':'targetMethodDescriptor'
            },
            'transformer': function(method) {
                return method;
            }
        }
    }

Ich habe es "coremodmethod" genannt, aber es scheint, dass dies eine beliebige Zeichenfolge sein kann. Andere "Ziel" und "Transformator" müssen dies sein.

Eine Methode namens mapMethod () ist in ASMAPI definiert und gibt den Namen der zugeordneten Methode aus dem Namen der verschleierten Methode zurück. Die "saveScreenshotRaw" -Methode der Klasse "net.minecraft.util.ScreenShotHelper" lautet beispielsweise "func_228051_b_" oder so ähnlich. Wenn Sie den Methodennamen mit dieser mapMethod () nicht erhalten, funktioniert er in der Entwicklungsumgebung, jedoch nicht in der realen Umgebung (oder umgekehrt).

Fügen Sie beim eigentlichen Schreiben den Namen der verschleierten Methode in das Argument "ASMAPI.mapMethod (" func_228051_b_ ")" ein.

Wählen Sie für den verschleierten Methodennamen die Zuordnung aus, die zu Ihrer Entwicklungsumgebung passt, aus dem üblichen MCP-Bot.

Übrigens gibt es einige Leute, die es nicht finden können, selbst wenn sie es durch MCP-Mapping suchen. Sie müssen den Methodennamen nicht mit mapMethod () abrufen, da sie bereits unter dem Namen ausgeführt werden, der sowohl in der Entwicklungsumgebung als auch in der realen Umgebung leicht zu verstehen ist.

"methodDesc" ist der übliche Deskriptor.

void hogehoge(int i, float f)

Wenn es sich um eine solche Methode handelt, lautet der Deskriptor "(IF) V".

Sowohl IDEA als auch Eclipse verfügen über ein Plug-In, das Java-Code im Byte-Code anzeigt. Ich denke, dass die Verwendung des Plug-Ins das Kopieren verbessert.

Tatsächliche Reorganisation

'transformer': function(method) {
    print("Enter transformer.");
    
    var arrayLength = method.instructions.size();
    var target_instruction = null;
    
    //search the target instruction from the entire instructions.
    for(var i = 0; i < arrayLength; ++i) {
        var instruction = method.instructions.get(i);
        if(instruction.name === "targetMethodName") {
            target_instruction = instruction;
            break;
        }
    }

    if(target_instruction == null) {
        print("Failed to detect target.");
        return method;
    }

    var toInject = new InsnList();
//  toInject.add(new InsnNode(Opcodes.POP));
    toInject.add(new InsnNode(Opcodes.RETURN));
    
    // Inject new instructions just after the target.
    method.instructions.insert(target_instruction, toInject);
    
    return method;
}

Ich persönlich denke, dass die Teile, die tatsächlich modifiziert werden, einfacher zu schreiben sind als zuvor.

Die Methode, die das Argument kreuzt, verfügt über ein Feld namens "Anweisungen", in das der Bytecode zeilenweise in ein Array eingegeben wird. Der Bytecode der Methode selbst lautet. Danach können Sie an jeder Stelle im Bytecode eine Rückgabe löschen, ändern oder einfügen.

Oben haben wir eine Änderung vorgenommen, die "sofort zurückkehrt, wenn eine bestimmte Methode aufgerufen wird". Drehen Sie Anweisungen primär nacheinander mit for, um die Zeile (dh Anweisungen) zu finden, in der die Zielmethode aufgerufen wird.

Wenn Sie die gesuchte Anweisung gefunden haben, fügen Sie eine neue Anweisung ein.

InsnList () ist eine Klasse zum Erstellen einer Reihe von Befehlspaaren, und jeder Befehl wird dieser Klasse hinzugefügt (). Hier wird als "neuer InsnNode (Opcodes.RETURN)" nur die Anweisung zur InsnList hinzugefügt, die "return" bedeutet.

Schließlich haben Anweisungen eine Methode namens insert (Argument 1, Argument 2), die die Anweisungen in die in Argument 2 angegebene insnList unmittelbar nach ** unmittelbar nach der in Argument 1 angegebenen Anweisung hinzufügt. Ich werde.

Hier wird "return" unmittelbar nach einer bestimmten Methode hinzugefügt.

Es ist wichtig zu beachten, dass wenn Sie coremod berührt haben, Sie wissen, dass die Frames inkonsistent werden und wahrscheinlich auch in der aktuellen Version abstürzen, wenn Sie so viel zurückgeben, wie Sie möchten. Ich denke, dass Werte, die in den Frame geladen werden, aber nicht mehr verwendet werden, die richtige Anzahl von Pops angezeigt werden müssen.

Andere

Am Ende

Ich dachte, ich würde ein Beispiel schreiben, aber es hat mir geholfen. Ich werde es wieder schreiben, wenn ich freie Kapazität habe.

Ich habe das Gefühl, dass es verschiedene Fehler und Missverständnisse gibt. Bitte lassen Sie mich wissen, wenn Sie es bemerken.

Vielen Dank für Ihre Beziehung.

Recommended Posts

Wie schreibe ich einen Core Mod in Minecraft Forge 1.15.2
Wie unterschreibe ich Minecraft MOD?
So schreiben Sie eine Datumsvergleichssuche in Rails
[Basic] So schreiben Sie ein Dockerfile Selbstlernend ②
So fügen Sie ein Video in Rails ein
[Einführung in Java] So schreiben Sie ein Java-Programm
So veröffentlichen Sie eine Bibliothek in jCenter
[SpringBoot] So schreiben Sie einen Controller-Test
Schienen: Wie man eine Rechenaufgabe schön schreibt
Füge Mods mit Minecraft Forge 1.15.2 Originalwaffen hinzu
[Java FX] So schreiben Sie Eclipse-Berechtigungen in build.gradle
[Rails] Wie schreibe ich, wenn ich eine Unterabfrage mache?
So zeigen Sie eine Webseite in Java an
So bearbeiten Sie eine Ebene mit Core ML Tools
So führen Sie eine djUnit-Aufgabe in Ant aus
So fügen Sie in Spring Boot einen Klassenpfad hinzu
So erstellen Sie ein Thema in Liferay 7 / DXP
So implementieren Sie eine ähnliche Funktion in Rails
So erstellen Sie einfach ein Pulldown mit Rails
Wie schreibe ich Java String # getBytes in Kotlin?
Hinweise zum Schreiben von Kommentaren auf Englisch
So generieren Sie automatisch einen Konstruktor in Eclipse
Wie schreibe ich Rails
Wie schreibe ich Mockito
So schreiben Sie eine Migrationsdatei
So löschen Sie alle Daten in einer bestimmten Tabelle
So erstellen Sie eine Java-Umgebung in nur 3 Sekunden
So schreiben Sie einen Komponententest für Spring Boot 2
java: Wie schreibe ich eine generische Typliste? [Hinweis]
So erstellen Sie ein Spring Boot-Projekt in IntelliJ
So erstellen Sie einen Daten-URI (base64) in Java
So starten Sie einen anderen Befehl in einem Ruby-Programm
So zeigen Sie eine Browser-Vorschau mit VS-Code an
[So fügen Sie ein Video mit Rails in haml ein]
So verspotten Sie einen Super-Methodenaufruf in PowerMock
So konvertieren Sie eine Datei in ein Byte-Array in Java
[Rails 6] So legen Sie ein Hintergrundbild in Rails [CSS] fest
[Rails] So laden Sie JavaScript in einer bestimmten Ansicht
Wie man guten Code schreibt
Wie schreibe ich einen Java-Kommentar
Wie hinterlasse ich einen Kommentar?
[Refactoring] So schreiben Sie Routing
Wie schreibe ich Junit 5 organisiert
Wie schreibe ich Rails Seed
So schreiben Sie ein benutzerorientiertes Programm (1)
Wie schreibe ich Rails Routing
So fügen Sie ein Video ein
So erstellen Sie eine Methode
So schreiben Sie eine Migration vom Rails-Datums- / Uhrzeittyp zum Datumstyp
[Forge] So registrieren Sie Ihre eigene Entität und Ihr Entity Render in 1.13.2