- JVM-Bytecode ist das kompakte Befehlssatz, den die Java-Virtuelle Maschine ausführt.
- Der Java-Compiler erzeugt Bytecode in
.class-Dateien, nicht in nativem Maschinencode. - Die gleiche
.class-Datei läuft auf jeder Plattform mit einer Java-Laufzeitumgebung, was der Sinn dieses Formats ist. - Mods überschreiben Bytecode, um das kompilierte Spiel zu verändern, ohne jemals Zugriff auf den Quellcode zu haben.
Der Bytecode der JVM ist der Befehlssatz, den die Java Virtual Machine ausführt. Wenn Sie Java-Quellcode kompilieren, erzeugt der Compiler keinen Maschinencode für Ihre CPU. Stattdessen erzeugt er Bytecode, ein kompaktes, portables Format, das in .class-Dateien gespeichert ist. Die JVM liest diese Anweisungen zur Laufzeit und wandelt sie bei Bedarf in nativen Code um.
Wo Opal passt
Opal ist ein Fabric Mod, und die Modding-Werkzeuge, auf denen es aufbaut, erledigen die Bytecode-Arbeit für Sie, sodass Sie keine Opcodes manuell anfassen müssen. Wenn Sie Verhalten in einfachem JavaScript hinzufügen möchten, siehe die Einführung in die Skripterstellung.
Wie Quellcode zu Bytecode wird
Eine .java-Datei ist Text, den eine Person schreibt. Eine .class-Datei ist die kompilierte Ausgabe, die die JVM ausführt. Der Compiler liest Ihren Quellcode, prüft ihn und schreibt für jede Klasse eine .class-Datei, die Bytecode-Anweisungen sowie einen Constant Pool, Methodentabellen und andere Metadaten enthält.
Bytecode ist an keinen bestimmten Prozessor gebunden. Die gleiche .class-Datei läuft unter Windows, macOS, Linux oder auf jedem System mit einer Java-Laufzeitumgebung, da die JVM die Schicht ist, die weiß, wie die Anweisungen auf der lokalen Maschine ausgeführt werden. Diese Portabilität ist der Sinn und Zweck des Formats.
Wie die Anweisungen aussehen
Bytecode ist ein stapelbasierter Befehlssatz. Die meisten Operationen legen Werte auf einen Operand-Stack, entfernt sie und legt ein Ergebnis ab. Es gibt keine Registerzuweisung, über die man nachdenken muss; das Modell ist absichtlich einfach, damit die JVM es schnell verifizieren und ausführen kann.
Einige Beispiel-Opcodes geben den Charakter wieder:
| Opcode | Was er tut |
|---|---|
iload | Legt eine lokale Integer-Variable auf den Stack |
iadd | Entfernt zwei Integers, legt ihre Summe ab |
invokevirtual | Ruft eine Instanzmethode auf |
getfield | Liest ein Instanzfeld |
return | Kehrt von einer Methode zurück |
Sie lesen oder schreiben diese selten manuell. Tools generieren und verbrauchen sie, was die Verwendung einer Bibliothek wie ASM ermöglicht.
Warum Bytecode für Mods wichtig ist
Bytecode ist wichtig, weil es ermöglicht, das Verhalten zu ändern, ohne den ursprünglichen Quellcode zu haben. Ein Minecraft-Mod hat fast nie den Quellcode des Spiels. Stattdessen hat er das kompilierte Spiel auf der Festplatte. Um eine Funktion hinzuzufügen oder zu verändern, liest ein Mod den vorhandenen Bytecode, schreibt ihn um und speist das Ergebnis zurück an die JVM, wenn die Klassen geladen werden.
So fügen Patching-Frameworks Hooks in Methoden ein, die sie nicht besitzen. Sie finden die richtige Stelle in einer kompilierten Methode und fügen einen Aufruf des Mod-Codes ein. Die Arbeit auf Bytecode-Ebene bedeutet, dass ein Mod sich an das Spiel anhängen kann, obwohl er nie eine Zeile des ursprünglichen Java-Codes sieht.
Wo ASM ins Spiel kommt
ASM ist eine Java-Bibliothek zum Lesen, Schreiben und Transformieren von Bytecode. Sie parst eine .class-Datei in Ereignisse oder einen Baum, ermöglicht die Modifikation der Anweisungen und gibt eine gültige .class-Datei aus. Sie ist klein, schnell und arbeitet eng mit dem Rohformat, weshalb höherwertige Modding-Tools darauf aufbauen, anstatt die Klassendateien selbst zu parsen.
Die meisten Modder rufen ASM nicht direkt auf. Sie verwenden eine höherwertige Schicht, die die Opcodes verbirgt und eine freundlichere Möglichkeit bietet, zu sagen: "Führe meinen Code am Anfang dieser Methode aus." Darunter bearbeitet diese Schicht Bytecode, oft über ASM.
FAQ
Nein. Maschinencode sind native Anweisungen für eine bestimmte CPU. Bytecode ist der eigene Befehlssatz der JVM, der von der JVM zur Laufzeit in Maschinencode übersetzt wird. Diese zusätzliche Ebene ermöglicht es, dass eine .class-Datei auf jeder Plattform ausgeführt werden kann.
Teilweise. Dekompiler rekonstruieren lesbaren Java-Code aus Bytecode und kommen bei einfachem Code recht nahe. Das Ergebnis ist nicht immer identisch mit dem Original, und Kommentare sowie einige lokale Namen fehlen, aber es reicht meistens aus, um dem Code folgen zu können.
Da Mods in der Regel nicht den Quellcode haben. Sie werden mit dem kompilierten Spiel ausgeliefert. Durch das Umschreiben von Bytecode kann ein Mod das Verhalten ändern, ohne jemals die ursprünglichen Java-Dateien zu berühren oder zu besitzen.
Normalerweise nicht. Modding-Frameworks und Skriptschichten übernehmen die Bytecode-Arbeit für Sie. Kenntnisse darüber sind hilfreich, wenn Sie einen kniffligen Patch debuggen oder Werkzeuge entwickeln.