fixes
This commit is contained in:
1 parent
83aea53e57
commit
aa4f6b6ddc
842 files changed
+47284
-91563
No files matched your search
@@ -0,0 +1,118 @@
|
||||
package dev.zprestige.prestige.inject;
|
||||
|
||||
import dev.zprestige.prestige.client.Prestige;
|
||||
import dev.zprestige.prestige.engine.EnginePlugin;
|
||||
import dev.zprestige.prestige.engine.EngineState;
|
||||
import dev.zprestige.prestige.hooks.Definer;
|
||||
import dev.zprestige.prestige.ui.mc.Entry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.lenni0451.classtransform.TransformerManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
/**
|
||||
* Entry point of the injected payload. prestige_native.dll appends this jar to the Fabric (Knot) class path, registers the
|
||||
* {@link NativeBridge} natives and calls {@link #start()}. Everything Fabric would normally do at launch for the Prestige mod
|
||||
* (Mixin anchors, entrypoints) is done here instead: the ClassTransform hooks are applied to the loaded classes with
|
||||
* {@link InPlaceTransformer} + JVMTI redefinition, then the mod entrypoints run on the client thread.
|
||||
*/
|
||||
public final class Bootstrap {
|
||||
private static boolean started;
|
||||
|
||||
private Bootstrap() {}
|
||||
|
||||
/** Called by the native loader on its worker thread once Minecraft is up. */
|
||||
public static synchronized void start() {
|
||||
if (started) return;
|
||||
started = true;
|
||||
try {
|
||||
log("waiting for the Minecraft client");
|
||||
waitForClient();
|
||||
Report report = install(NativeBridge::classBytes, NativeBridge::redefine);
|
||||
log("hooks installed: " + report.redefined + " classes, " + report.moved + " handlers moved" + (report.failed.isEmpty() ? "" : ", FAILED " + report.failed));
|
||||
if (!report.failed.isEmpty()) throw new IllegalStateException("could not hook " + report.failed);
|
||||
log("initialising Prestige on the client thread");
|
||||
MinecraftClient.getInstance().execute(Bootstrap::initClient);
|
||||
} catch (Throwable t) {
|
||||
log("start failed: " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
/** The loader may inject while the game is still starting: wait until the client and its window exist. */
|
||||
private static void waitForClient() throws InterruptedException {
|
||||
for (int i = 0; i < 6000; i++) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client != null && client.getWindow() != null) return;
|
||||
Thread.sleep(50);
|
||||
}
|
||||
throw new IllegalStateException("Minecraft client did not start");
|
||||
}
|
||||
|
||||
public static final class Report {
|
||||
public int redefined, moved;
|
||||
public final List<String> failed = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all injection-mode hooks. The byte source and the redefiner are parameters so the transformation can be exercised
|
||||
* without the native library.
|
||||
*/
|
||||
public static Report install(Function<Class<?>, byte[]> classBytes, ToIntBiFunction<Class<?>, byte[]> redefine) throws Exception {
|
||||
String namespace = FabricLoader.getInstance().getMappingResolver().getCurrentRuntimeNamespace();
|
||||
List<String> transformers = new ArrayList<>(EnginePlugin.lines("prestige-engine/transformers.list"));
|
||||
transformers.addAll(EnginePlugin.lines("prestige-engine/inject-transformers.list"));
|
||||
TransformerManager manager = EnginePlugin.createManager(namespace, transformers);
|
||||
ClassLoader loader = Bootstrap.class.getClassLoader();
|
||||
InPlaceTransformer transformer = new InPlaceTransformer(manager, loader);
|
||||
EngineState.installed = true;
|
||||
|
||||
Report report = new Report();
|
||||
int total = manager.getTransformedClasses().size(), done = 0;
|
||||
log("transforming " + total + " hook targets");
|
||||
for (String target : manager.getTransformedClasses()) {
|
||||
if (done > 0 && done % 10 == 0) log("transformed " + done + "/" + total);
|
||||
done++;
|
||||
try {
|
||||
Class<?> type = Class.forName(target, false, loader);
|
||||
byte[] current = classBytes.apply(type);
|
||||
if (current == null) throw new IllegalStateException("no class bytes");
|
||||
InPlaceTransformer.Result result = transformer.transform(target, current);
|
||||
for (byte[] helper : result.helpers().values()) Definer.define(helper);
|
||||
int code = redefine.applyAsInt(type, result.target());
|
||||
if (code != 0) throw new IllegalStateException("redefine returned JVMTI error " + code);
|
||||
report.redefined++;
|
||||
report.moved += result.movedMethods();
|
||||
} catch (Throwable t) {
|
||||
report.failed.add(target);
|
||||
log("hook failed for " + target + ": " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
private static void initClient() {
|
||||
try {
|
||||
new Prestige().onInitialize();
|
||||
new Entry().onInitializeClient();
|
||||
log("client initialised");
|
||||
} catch (Throwable t) {
|
||||
log("client init failed: " + t);
|
||||
t.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
private static void log(String message) {
|
||||
System.out.println("[Prestige-Inject] " + message);
|
||||
try {
|
||||
NativeBridge.log(message);
|
||||
} catch (UnsatisfiedLinkError ignored) {
|
||||
// not injected (dev run): the console line above is enough
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user