Je vais expliquer brièvement comment écrire un mod de base dans l'environnement Minecraft Forge 1.15.2. Qu'est-ce que l'ASM? Qu'est-ce qu'un code d'octet? Je ne vais pas l'expliquer ici. Excusez-moi. Je n'expliquerai pas non plus Javascript. Excusez-moi.
Cependant, si vous êtes un moddeur qui utilise des mods de base, vous pouvez écrire avec un sentiment.
Forge 1.13 ou version ultérieure fonctionnera probablement de la même manière, mais je ne l'ai pas vérifié.
J'ai beaucoup fait référence à cette page et à l'exemple de code. Je suis profondément reconnaissant à mes prédécesseurs. 99.99 - Coremod
Toujours facile. "coremod" est l'un des mécanismes fournis par Forge, qui vous permet de réécrire le code Java de Minecraft lui-même (il y a un malentendu). La bibliothèque utilisée pour cela est ASM. De plus, le code lui-même n'est pas le code humain que vous écrivez habituellement, mais le code d'octet qui a été compilé. Dans tous les cas, si vous écrivez ASM, vous pourrez tout faire. Cependant, la concurrence avec d'autres mods peut augmenter.
Ce pdf est fortement recommandé (mais long) pour étudier l'ASM. ASM 4.0 A Java bytecode engineering library
Minecraft: 1.15.2 Minecraft Forge: 1.15.2-31.2.0
Auparavant, coremod était écrit en code Java, mais à partir de la 1.13, il sera écrit en ** Javascript **. Il est normal de penser que l'ASM lui-même est écrit de la même manière qu'avant (probablement).
Au moins deux fichiers sont requis.
coremods.json Placez-le directement sous ** main / java / resources / META-INF / folder **. Le nom du fichier devrait probablement être "coremods.json".
Le contenu est comme ci-dessous.
coremods.js
{
"TestMod Transformer": "testmod-transformer.js"
}
La clé du tableau ("Test Mod Transformer") est probablement n'importe quoi. Je pense que peu importe le cas. Pour la valeur, écrivez l'emplacement du fichier js que vous allez écrire.
Si vous écrivez simplement le nom du fichier comme ci-dessus, vous spécifiez le fichier dans ** main / java / resources / directement sous **. Je n'ai pas essayé si le chemin absolu ou le chemin relatif fonctionne, mais je pense que le chemin relatif semble fonctionner.
coremods.js
{
"TestMod Transformer": "testmod-transformer.js",
"TestMod Transformer2": "testmod-transformer2.js"
}
Vous pouvez également spécifier plusieurs fichiers javascript à la fois.
Par souci de clarté, mettons-le directement sous main / java / resources /. Si vous modifiez l'emplacement, veuillez modifier la valeur du contenu de coremods.json ci-dessus. Le js qui écrit réellement ASM ressemble à ce qui suit.
testmod-transformer.js
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'METHOD',
'class': 'your.target.class',
'methodName': 'targetMethodName',
'methodDesc': 'targetMethodDescriptor'
},
'transformer': function(method) {
//Écrivez le traitement ASM ici.
return method;
}
}
}
}
Dans le fichier js, vous devez écrire une fonction nommée "initializeCoreMod ()" qui renvoie json. Ce n'est pas grave si vous copiez et collez sur le dessus. Si vous écrivez diverses choses dans la section «Écrire le traitement ASM ici» ci-dessus, cela est terminé.
Je vous remercie pour votre travail acharné. Alors, tu viens habituellement ici? Cela semble être dit, alors j'écrirai un peu plus. Si vous comprenez ce qui précède, n'hésitez pas à faire le reste.
Vous pouvez spécifier trois types de cibles: "FIELD", "METHOD" et "CLASS". Cela interfère avec les champs, les méthodes et les classes, respectivement. En d'autres termes, vous pouvez écrire les trois manières suivantes.
testmod-transformer.js
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'FIELD',
'class': 'your.target.class',
'fieldName': 'targetFieldName'
},
'transformer': function(field) {
//Écrivez le traitement ASM ici.
return field;
}
}
}
}
testmod-transformer.js
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'METHOD',
'class': 'your.target.class',
'methodName': 'targetMethodName',
'methodDesc': 'targetMethodDescriptor'
},
'transformer': function(method) {
//Écrivez le traitement ASM ici.
return method;
}
}
}
}
testmod-transformer.js
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'CLASS',
'class': 'your.target.class'
},
'transformer': function(class) {
//Écrivez le traitement ASM ici.
return class;
}
}
}
}
Les valeurs requises dans "cible" sont comme ci-dessus. Cependant, pour la raison personnelle que les champs peuvent être interférés à partir de Java avec l'assistant de réflexion, et il semble que l'interférence avec la méthode soit assez souvent sans interférer avec la classe elle-même, ce qui suit est une modification de FIELD et CLASS. Ne touchera pas. Quelqu'un s'il vous plaît.
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;
}
}
}
}
Je pense que les perspectives se sont considérablement améliorées. Je vais expliquer dans l'ordre.
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");
Je ne sais pas de quel genre de magie il s'agit, mais si vous mettez le nom habituel de la classe de destination d'importation dans l'argument de "Java.type ()", vous pouvez l'utiliser comme s'il s'agissait de Java (explication divers). Si Opcodes arrive, je sens qu'il y a déjà 100 personnes. Mais j'ai des problèmes avec l'achèvement du code qui ne fonctionne pas. Si quelqu'un sait comment compléter, faites-le moi savoir. Je suis sûr qu'ASMAPI et Opcodes utiliseront n'importe quelle modification, mais je pense que c'est votre préférence d'appeler d'autres classes. Le coremod actuel utilise ce qu'on appelle l'API d'arborescence, veuillez donc vérifier séparément pour voir quelles classes sont disponibles.
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;
}
}
}
Je l'ai nommé "coremodmethod", mais il semble que cela puisse être n'importe quelle chaîne de caractères. Les autres «cibles» et «transformateurs» doivent être ceci.
Une méthode appelée mapMethod () est définie dans ASMAPI et renvoie le nom de la méthode mappée à partir du nom de la méthode masquée. Par exemple, la méthode "saveScreenshotRaw" de la classe "net.minecraft.util.ScreenShotHelper" est "func_228051_b_" ou quelque chose comme ça. Si vous n'obtenez pas le nom de la méthode en utilisant cette mapMethod (), cela fonctionnera dans l'environnement de développement mais pas dans l'environnement réel (ou vice versa).
Lors de l'écriture, placez le nom de la méthode masquée dans l'argument tel que "ASMAPI.mapMethod (" func_228051_b_ ")".
Pour le nom de la méthode après obfuscation, sélectionnez le mappage qui convient à votre environnement de développement à partir du [Bot MCP] habituel (http://export.mcpbot.bspk.rs/).
À propos, il y a des gens qui ne peuvent pas le trouver même s'ils le recherchent par cartographie MCP. Vous n'avez pas besoin de récupérer le nom de la méthode avec mapMethod () car elles s'exécutent déjà sous le nom qui est facile à comprendre à la fois dans l'environnement de développement et dans l'environnement réel.
"methodDesc" est le descripteur habituel.
void hogehoge(int i, float f)
S'il s'agit d'une méthode comme celle-là, le descripteur sera "(IF) V".
IDEA et Eclipse ont tous deux un plug-in qui affiche le code Java en byte code, donc je pense que son utilisation améliorera la copie.
'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;
}
Je pense personnellement que les parties qui sont réellement modifiées sont plus faciles à écrire qu'auparavant.
La méthode qui croise l'argument a un champ appelé instructions, dans lequel le code d'octet est entré ligne par ligne dans un tableau. Le bytecode de la méthode elle-même est. Après cela, vous pouvez supprimer, modifier ou insérer un retour à n'importe quelle position dans le code d'octet.
Dans ce qui précède, nous avons fait une modification qui "revient sur place quand une certaine méthode est appelée". Commencez par tourner les instructions une par une avec for pour trouver la ligne (c'est-à-dire les instructions) où la méthode cible est appelée.
Lorsque vous trouvez l'instruction que vous recherchez, insérez une nouvelle instruction.
InsnList () est une classe pour créer une série de paires d'instructions, et chaque instruction est ajoutée () à cette classe. Ici, en tant que "new InsnNode (Opcodes.RETURN)", seule l'instruction qui signifie "return" est ajoutée à l'InsnList.
Enfin, les instructions ont une méthode appelée insert (argument 1, argument 2), qui ajoute les instructions à l'intérieur de la insnList spécifiée dans l'argument 2 immédiatement après ** immédiatement après l'instruction spécifiée dans l'argument 1. Je vais.
Ici, c'est le processus d'ajout de «retour» immédiatement après une méthode spécifique.
Il est important de noter que si vous avez touché coremod, vous saurez que si vous revenez autant que vous le souhaitez, les cadres deviendront incohérents et planteront probablement également dans la version actuelle. Je pense que les valeurs qui sont chargées dans le cadre mais qui ne sont plus utilisées doivent être sautées le bon nombre de fois.
--Je ne connais pas la raison, mais vous ne pouvez pas utiliser let ou const lors de la déclaration d'une variable. Une erreur se produira. Utilisez var. --print () sort sur la console lorsque vous déboguez et démarrez Micra dans l'environnement de développement, mais il semble qu'il n'apparaisse nulle part dans l'environnement réel. ――Il semble que vous puissiez renvoyer plusieurs transformateurs avec un fichier javascript, mais je ne sais pas comment le faire.
Je pensais écrire un exemple, mais cela m'a aidé. Je l'écrirai à nouveau si j'ai une certaine capacité de réserve.
Je pense qu'il y a diverses erreurs et malentendus, alors faites-le moi savoir si vous remarquez.
Merci pour votre relation.
Recommended Posts