cc1eaece40
Главное: модлоадер физически не умел запускать больше одного мода. Родной
загрузчик игры запоминает тик-хук в статическом поле, которое перезаписывается
на каждой загрузке:
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 на все моды,
и такой тест не мог поймать проблему с изоляцией.
40 lines
2.0 KiB
Plaintext
40 lines
2.0 KiB
Plaintext
rsxprofile 1
|
|
build 9ad8c4aac5d835fc 9ad8c4aac5d835fc38f68d0648ffd67c809f8342e88b9636ee36205f8f96bf35 3759 7695 59020857 1792011090
|
|
loader ilxq super special.Main
|
|
patch.a2 wsygzaib joml.fastmath
|
|
patch.a6 pvdafug snooper pC
|
|
class net.minecraft.client.Minecraft wwk
|
|
field net.minecraft.client.Minecraft player zb Lwpqgx;
|
|
field net.minecraft.client.Minecraft world ta Lwwhgz;
|
|
field net.minecraft.client.Minecraft currentScreen S Lbsh;
|
|
field net.minecraft.client.Minecraft timer Zb Lmlyn;
|
|
field net.minecraft.client.Minecraft instance WA Lwwk;
|
|
field net.minecraft.client.Minecraft renderGlobal jC Ldqtrv;
|
|
field net.minecraft.client.Minecraft gameSettings y Ldmoadr;
|
|
field net.minecraft.client.Minecraft playerController fa Ltdsh;
|
|
method net.minecraft.client.Minecraft getMinecraft super ()Lwwk;
|
|
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
|
|
class net.minecraft.client.settings.GameSettings qoa
|
|
override.server catch
|