Несколько модов одновременно и живая работа с игрой
Главное: модлоадер физически не умел запускать больше одного мода. Родной
загрузчик игры запоминает тик-хук в статическом поле, которое перезаписывается
на каждой загрузке:
do = new ilxq(); // свой загрузчик на мод
int = method2; // статическое поле: помнит только ПОСЛЕДНИЙ мод
Из-за этого getRunnableList() вызывался у одного мода. Остальные грузились,
их onLoad отрабатывал, задачи висели в очереди - и их никто никогда не
разбирал. Молча.
Чинится без обфускации: в мосте одна очередь на всех и один drain. Какой бы
special.Main игра ни позвала, он выполняет работу всех модов. Владелец задачи
запоминается в ThreadLocal в момент onLoad, поэтому в логе видно, чей код
упал. Счетчик падений тоже переехал с записи в очереди на саму задачу: очередь
чистится каждый тик, и счетчик на ней обнулялся бы раньше, чем дошел до
предела.
API:
- Rsx.Player и Rsx.World: getName/getX/getY/getZ/getHealth/getMaxHealth/
isAlive/isDead и getTime/isClient. Раньше player() отдавал Object, и мод
вытаскивал имя из toString() обфусцированного класса.
- События: Rsx.on("tick"|"worldChange"|"playerJoin"|"playerLeave"|
"screenOpen"|"screenClose"). Заменяют polling.
- Rsx.everyTicks(n) - периодичность по тикам, не плывет и не двоит после лага.
- RsxMod.apiVersion() и onError(): мод заявляет версию API и сам решает, что
делать с падением. Без этого сломанный мод заливал лог.
- Задача, упавшая 5 раз подряд, отбрасывается целиком.
- Rsx.error() и именованные every/post - чтобы в логе было видно, чё это.
Профиль перегенерирован: в него попали члены Entity (posX/posY/posZ/isDead,
getName/getEntityId/isEntityAlive), EntityLivingBase (здоровье) и World
(isRemote/getWorldTime), которых раньше не было. Сгенерированы по SRG-именам
через make_profile.py, поэтому переживут очередную реобфускацию.
Тест-стенд переписан под три мода и воспроизводит обе особенности вендорного
загрузчика: новый classloader на jar и перезаписываемое статическое поле.
Контрольный прогон с отключённым drain роняет 10 проверок из 17, так что
проверки действительно завязаны на этот механизм.
Заодно: сломанный фейковый загрузчик делил один classloader на все моды,
и такой тест не мог поймать проблему с изоляцией.
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"classes": {
|
||||
"net.minecraft.client.Minecraft": {
|
||||
"obf": "wwk",
|
||||
"fields": {
|
||||
"player": {
|
||||
"obf": "zb",
|
||||
"desc": "Lwpqgx;",
|
||||
"kind": "f",
|
||||
"srg": "field_71439_g"
|
||||
},
|
||||
"world": {
|
||||
"obf": "ta",
|
||||
"desc": "Lwwhgz;",
|
||||
"kind": "f",
|
||||
"srg": "field_71441_e"
|
||||
},
|
||||
"currentScreen": {
|
||||
"obf": "S",
|
||||
"desc": "Lbsh;",
|
||||
"kind": "f",
|
||||
"srg": "field_71462_r"
|
||||
},
|
||||
"timer": {
|
||||
"obf": "Zb",
|
||||
"desc": "Lmlyn;",
|
||||
"kind": "f",
|
||||
"srg": "field_71428_T"
|
||||
},
|
||||
"instance": {
|
||||
"obf": "WA",
|
||||
"desc": "Lwwk;",
|
||||
"kind": "f",
|
||||
"srg": "field_71432_P"
|
||||
},
|
||||
"renderGlobal": {
|
||||
"obf": "jC",
|
||||
"desc": "Ldqtrv;",
|
||||
"kind": "f",
|
||||
"srg": "field_71438_f"
|
||||
},
|
||||
"gameSettings": {
|
||||
"obf": "y",
|
||||
"desc": "Ldmoadr;",
|
||||
"kind": "f",
|
||||
"srg": "field_71474_y"
|
||||
},
|
||||
"playerController": {
|
||||
"obf": "fa",
|
||||
"desc": "Ltdsh;",
|
||||
"kind": "f",
|
||||
"srg": "field_71442_b"
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"getMinecraft": {
|
||||
"obf": "super",
|
||||
"desc": "()Lwwk;",
|
||||
"kind": "m",
|
||||
"srg": "func_71410_x"
|
||||
},
|
||||
"displayGuiScreen": {
|
||||
"obf": "super",
|
||||
"desc": "(Lbsh;)V",
|
||||
"kind": "m",
|
||||
"srg": "func_147108_a"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.minecraft.client.entity.EntityPlayerSP": {
|
||||
"obf": "wpqgx"
|
||||
},
|
||||
"net.minecraft.entity.player.EntityPlayer": {
|
||||
"obf": "obzsyig"
|
||||
},
|
||||
"net.minecraft.world.World": {
|
||||
"obf": "oisywi",
|
||||
"fields": {
|
||||
"isRemote": {
|
||||
"obf": "byte",
|
||||
"desc": "Z",
|
||||
"kind": "f",
|
||||
"srg": "field_72995_K"
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"getWorldTime": {
|
||||
"obf": "super",
|
||||
"desc": "()J",
|
||||
"kind": "m",
|
||||
"srg": "func_72820_D"
|
||||
},
|
||||
"getWorldInfo": {
|
||||
"obf": "super",
|
||||
"desc": "()Lcilc;",
|
||||
"kind": "m",
|
||||
"srg": "func_72912_H"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.minecraft.client.multiplayer.WorldClient": {
|
||||
"obf": "wwhgz"
|
||||
},
|
||||
"net.minecraft.entity.Entity": {
|
||||
"obf": "ddvqd",
|
||||
"fields": {
|
||||
"posX": {
|
||||
"obf": "g",
|
||||
"desc": "D",
|
||||
"kind": "f",
|
||||
"srg": "field_70165_t"
|
||||
},
|
||||
"posY": {
|
||||
"obf": "F",
|
||||
"desc": "D",
|
||||
"kind": "f",
|
||||
"srg": "field_70163_u"
|
||||
},
|
||||
"posZ": {
|
||||
"obf": "a",
|
||||
"desc": "D",
|
||||
"kind": "f",
|
||||
"srg": "field_70161_v"
|
||||
},
|
||||
"isDead": {
|
||||
"obf": "e",
|
||||
"desc": "Z",
|
||||
"kind": "f",
|
||||
"srg": "field_70128_L"
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"getName": {
|
||||
"obf": "super",
|
||||
"desc": "()Ljava/lang/String;",
|
||||
"kind": "m",
|
||||
"srg": "func_70005_c_"
|
||||
},
|
||||
"getEntityId": {
|
||||
"obf": "for",
|
||||
"desc": "()I",
|
||||
"kind": "m",
|
||||
"srg": "func_145782_y"
|
||||
},
|
||||
"isEntityAlive": {
|
||||
"obf": "switch",
|
||||
"desc": "()Z",
|
||||
"kind": "m",
|
||||
"srg": "func_70089_S"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.minecraft.entity.EntityLivingBase": {
|
||||
"obf": "bgtojhxo",
|
||||
"methods": {
|
||||
"getHealth": {
|
||||
"obf": "byte",
|
||||
"desc": "()F",
|
||||
"kind": "m",
|
||||
"srg": "func_110143_aJ"
|
||||
},
|
||||
"getMaxHealth": {
|
||||
"obf": "if",
|
||||
"desc": "()F",
|
||||
"kind": "m",
|
||||
"srg": "func_110138_aP"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.minecraft.network.PacketBuffer": {
|
||||
"obf": "dcuglw"
|
||||
},
|
||||
"net.minecraft.client.multiplayer.PlayerControllerMP": {
|
||||
"obf": "tdsh"
|
||||
},
|
||||
"net.minecraft.client.gui.GuiScreen": {
|
||||
"obf": "bsh"
|
||||
},
|
||||
"net.minecraft.client.settings.GameSettings": {
|
||||
"obf": "qoa"
|
||||
}
|
||||
},
|
||||
"bridge": {
|
||||
"loader": {
|
||||
"obf": "ilxq",
|
||||
"loadMethod": "super",
|
||||
"entry": "special.Main"
|
||||
},
|
||||
"patches": {
|
||||
"a2": {
|
||||
"class": "wsygzaib",
|
||||
"property": "joml.fastmath"
|
||||
},
|
||||
"a6": {
|
||||
"class": "pvdafug",
|
||||
"clientField": "snooper",
|
||||
"obf": "pC",
|
||||
"clientFieldObf": "pC"
|
||||
}
|
||||
}
|
||||
},
|
||||
"missing": [
|
||||
"net.minecraft.client.Minecraft#snooper",
|
||||
"net.minecraft.network.NetworkManager"
|
||||
],
|
||||
"build": {
|
||||
"id": "9ad8c4aac5d835fc",
|
||||
"gameJarSha256": "9ad8c4aac5d835fc38f68d0648ffd67c809f8342e88b9636ee36205f8f96bf35",
|
||||
"jarSize": 59020857,
|
||||
"jarMtime": 1792011090,
|
||||
"classes": 3759,
|
||||
"totalClasses": 7695,
|
||||
"generated": "2026-10-17 02:15"
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,21 @@ method net.minecraft.client.Minecraft displayGuiScreen super (Lbsh;)V
|
||||
class net.minecraft.client.entity.EntityPlayerSP wpqgx
|
||||
class net.minecraft.entity.player.EntityPlayer obzsyig
|
||||
class net.minecraft.world.World oisywi
|
||||
field net.minecraft.world.World isRemote byte Z
|
||||
method net.minecraft.world.World getWorldTime super ()J
|
||||
method net.minecraft.world.World getWorldInfo super ()Lcilc;
|
||||
class net.minecraft.client.multiplayer.WorldClient wwhgz
|
||||
class net.minecraft.entity.Entity ddvqd
|
||||
field net.minecraft.entity.Entity posX g D
|
||||
field net.minecraft.entity.Entity posY F D
|
||||
field net.minecraft.entity.Entity posZ a D
|
||||
field net.minecraft.entity.Entity isDead e Z
|
||||
method net.minecraft.entity.Entity getName super ()Ljava/lang/String;
|
||||
method net.minecraft.entity.Entity getEntityId for ()I
|
||||
method net.minecraft.entity.Entity isEntityAlive switch ()Z
|
||||
class net.minecraft.entity.EntityLivingBase bgtojhxo
|
||||
method net.minecraft.entity.EntityLivingBase getHealth byte ()F
|
||||
method net.minecraft.entity.EntityLivingBase getMaxHealth if ()F
|
||||
class net.minecraft.network.PacketBuffer dcuglw
|
||||
class net.minecraft.client.multiplayer.PlayerControllerMP tdsh
|
||||
class net.minecraft.client.gui.GuiScreen bsh
|
||||
|
||||
Reference in New Issue
Block a user