Files
OpenZen/src/main/java/shit/zen/patch/MinecraftPatch.java
T
Shirona1337andClaude Opus 4.7 c31c6a873a feat(dll): add hot-injection DLL path with embedded GUI loader
Ship a self-contained OpenZenLoader.exe (OpenZen.dll embedded as
RCDATA) that injects into a running Minecraft 1.20.1 Forge
javaw.exe and brings up the Zen client without -javaagent or a
mods/ entry. The existing Forge mod path stays intact.

Native (native/):
  - DLL: JNI_GetCreatedJavaVMs + JVMTI late-attach via the JDK's
    instrument.dll Agent_OnAttach to obtain a real Instrumentation;
    URLClassLoader on the game loader loads a single bridge class.
  - Loader EXE: Win32 listview of javaw.exe/java.exe processes
    with window titles, CreateRemoteThread+LoadLibraryW injection,
    embedded DLL extracted to %TEMP%\OpenZenLoader at runtime.

Java:
  - shit.zen.dll.GameLoaderBridge re-defines every jar class onto
    the Forge GameClassLoader (fixed-point retry for super-class
    deps) and extracts non-class resources to
    %TEMP%\openzen-resources-<pid>, exposed via the
    openzen.resources system property.
  - shit.zen.dll.DllBootstrap only runs Bootstrap.init +
    registerPatches + installPatchesAndRetransform; ZenClient
    construction stays on MinecraftPatch.onTick lazy-init.
  - shit.zen.asm.Bootstrap parses mapping.srg, detects SRG vs
    mojmap runtime via Minecraft.tick reflection, exposes
    remapMethod/remapField used at 5 PatchTransformer match
    points and from ReflectionUtil.
  - ReflectionUtil.resolveField walks the superclass chain trying
    SRG then mojmap on each level so e.g. activeEffects on
    LocalPlayer resolves on LivingEntity.
  - shit.zen.utils.misc.Assets unifies resource lookup with a
    fallback to openzen.resources; fonts, cloud assets and WebUI
    static files route through it.
  - PatchAgent.installPatchesAndRetransform is now idempotent so
    DllBootstrap and ZenClient.init can both call it safely.

Build:
  - ./gradlew jar   forge mod jar (unchanged)
  - ./gradlew dll   single-file Loader EXE in build/dist
  - CMake auto-located via vswhere when VS 2022 ships it.
  - README documents both paths and required toolchain.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-23 09:06:24 +08:00

143 lines
5.6 KiB
Java

package shit.zen.patch;
import asm.patchify.annotation.At;
import asm.patchify.annotation.Inject;
import asm.patchify.annotation.Patch;
import asm.patchify.annotation.Slice;
import asm.patchify.annotation.WrapInvoke;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import shit.zen.ClientBase;
import shit.zen.ZenClient;
import shit.zen.asm.Invocation;
import shit.zen.event.impl.DisconnectEvent;
import shit.zen.event.impl.PostMotionEvent;
import shit.zen.event.impl.PreMotionEvent;
import shit.zen.event.impl.TickEvent;
import shit.zen.modules.impl.movement.FastUse;
import shit.zen.modules.impl.render.ESP;
import shit.zen.render.Renderer;
@Patch(Minecraft.class)
public class MinecraftPatch {
public static volatile boolean initialized = false;
private static HitResult savedHitResult;
public static void markInitialized() {
initialized = true;
}
@Inject(method = "tick", desc = "()V")
public static void onTick(Minecraft minecraft, CallbackInfo callbackInfo) throws Throwable {
if (!initialized) {
synchronized (MinecraftPatch.class) {
if (!initialized) {
ClientBase.mc = ZenClient.getMcInstance();
ClientBase.isLoading = true;
ModList.get().getMods().removeIf(modInfo -> modInfo.getModId().equals("hey"));
List<IModFileInfo> toRemove = new ArrayList<>();
for (IModFileInfo modFile : ModList.get().getModFiles()) {
for (IModInfo modInfo : modFile.getMods()) {
if (modInfo.getModId().equals("hey")) {
toRemove.add(modFile);
}
}
}
ModList.get().getModFiles().removeAll(toRemove);
new ZenClient();
initialized = true;
}
}
}
if (ZenClient.isReady()) {
ZenClient.serverTickRate = 1.0f;
ClientBase.yaw = minecraft.player.getYRot();
ZenClient.instance.getEventBus().call(new TickEvent());
}
}
@Inject(method = "tick", desc = "()V", at = @At(At.Type.TAIL))
public static void onTickPost(Minecraft minecraft, CallbackInfo callbackInfo) throws Throwable {
if (ZenClient.isReady()) {
ZenClient.instance.getEventBus().call(new PostMotionEvent());
}
}
@Inject(method = "close", desc = "()V", at = @At(At.Type.HEAD))
public static void onClose(Minecraft minecraft, CallbackInfo callbackInfo) {
ZenClient.getInstance().shutdown();
}
@Inject(method = "setLevel", desc = "(Lnet/minecraft/client/multiplayer/ClientLevel;)V")
public static void onSetLevel(ClientLevel level, CallbackInfo callbackInfo) {
if (ZenClient.isReady()) {
ZenClient.getInstance().getEventBus().call(new DisconnectEvent());
}
}
@Inject(
method = "handleKeybinds",
desc = "()V",
at = @At(value = At.Type.BEFORE_INVOKE, method = "net/minecraft/client/player/LocalPlayer/isUsingItem", desc = "()Z"),
slice = @Slice(startIndex = 1, endIndex = 1)
)
public static void onHandleKeybinds(Minecraft minecraft, CallbackInfo callbackInfo) {
if (ZenClient.isReady()) {
PreMotionEvent event = new PreMotionEvent();
ZenClient.getInstance().getEventBus().call(event);
if (event.isCancelled()) {
callbackInfo.cancel();
}
}
}
@Inject(method = "startUseItem", desc = "()V", at = @At(At.Type.HEAD))
public static void onStartUseItemPre(Minecraft minecraft, CallbackInfo callbackInfo) {
if (FastUse.isBlocking(minecraft)) {
savedHitResult = minecraft.hitResult;
Vec3 location = savedHitResult.getLocation();
minecraft.hitResult = BlockHitResult.miss(location, Direction.DOWN, BlockPos.containing(location));
}
}
@Inject(method = "startUseItem", desc = "()V", at = @At(At.Type.TAIL))
public static void onStartUseItemPost(Minecraft minecraft, CallbackInfo callbackInfo) {
if (savedHitResult != null) {
minecraft.hitResult = savedHitResult;
savedHitResult = null;
}
}
@WrapInvoke(
method = "shouldEntityAppearGlowing",
desc = "(Lnet/minecraft/world/entity/Entity;)Z",
target = "net/minecraft/world/entity/Entity/isCurrentlyGlowing",
targetDesc = "()Z"
)
public static boolean onShouldEntityGlow(Minecraft minecraft, Entity entity, Invocation<Entity, Boolean> original) throws Exception {
if (ZenClient.isReady()
&& ZenClient.instance.getModuleManager() != null
&& ESP.INSTANCE != null
&& ESP.INSTANCE.isGlowing(entity)) {
return true;
}
return original.call();
}
@Inject(method = "resizeDisplay", desc = "()V", at = @At(At.Type.TAIL))
public static void onResizeDisplay(Minecraft minecraft, CallbackInfo callbackInfo) {
Renderer.setGuiScaleVerified((float) minecraft.getWindow().getGuiScale());
}
}