Модлоадер RustexRemake: агент, патч лаунчера, установщик и сборка
Моды грузятся через -javaagent, game.jar не патчится, переживают ре-обфускацию. Что внутри: - агент rsx-bridge.jar: профиль сборки игры, патчи A2/A6, загрузка модов через родной загрузчик игры (ilxq -> special.Main) - патч лаунчера: окно выбора «С модами / Ванилла» по кнопке «Играть» и защита от автоапдейта лаунчера - install/uninstall: всё ставится в %LOCALAPPDATA%\rsx-mod, пути не хардкодятся - build: три готовых артефакта в dist, нужен только JDK - testserver: локальный сервер, принимает любой ник и токен Проверено на живом клиенте: мод грузится и выполняется в игровом потоке, в логе видно игрока и мир.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package net.rustexremake.api;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import rsx.ModBridge;
|
||||
import rsx.Resolver;
|
||||
|
||||
/**
|
||||
* Stable API for mods.
|
||||
*
|
||||
* Nothing in this class mentions an obfuscated name. A mod asks for
|
||||
* "net.minecraft.client.Minecraft" and the bridge looks up what that class is
|
||||
* called in the build that is currently installed, so a mod compiled once keeps
|
||||
* working after the vendor renames everything again.
|
||||
*/
|
||||
public final class Rsx {
|
||||
|
||||
private Rsx() {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- identity
|
||||
public static String buildId() {
|
||||
return Resolver.profile() == null ? "unknown"
|
||||
: Resolver.profile().buildId;
|
||||
}
|
||||
|
||||
public static String describe(String logical) {
|
||||
return Resolver.profile() == null ? logical
|
||||
: Resolver.profile().describe(logical);
|
||||
}
|
||||
|
||||
public static boolean ready() {
|
||||
return Resolver.cls("net.minecraft.client.Minecraft") != null;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- accessors
|
||||
public static Object minecraft() {
|
||||
return Resolver.get("net.minecraft.client.Minecraft", "instance");
|
||||
}
|
||||
|
||||
public static Object player() {
|
||||
return Resolver.get("net.minecraft.client.Minecraft", "player", minecraft());
|
||||
}
|
||||
|
||||
public static Object world() {
|
||||
return Resolver.get("net.minecraft.client.Minecraft", "world", minecraft());
|
||||
}
|
||||
|
||||
public static Object currentScreen() {
|
||||
return Resolver.get("net.minecraft.client.Minecraft", "currentScreen", minecraft());
|
||||
}
|
||||
|
||||
public static Class<?> classOf(String logical) {
|
||||
return Resolver.cls(logical);
|
||||
}
|
||||
|
||||
public static Object get(String logicalClass, String member) {
|
||||
return Resolver.get(logicalClass, member);
|
||||
}
|
||||
|
||||
public static Object get(String logicalClass, String member, Object instance) {
|
||||
return Resolver.get(logicalClass, member, instance);
|
||||
}
|
||||
|
||||
public static void set(String logicalClass, String member, Object instance,
|
||||
Object value) {
|
||||
Resolver.set(logicalClass, member, instance, value);
|
||||
}
|
||||
|
||||
public static Object call(String logicalClass, String member, Object... args) {
|
||||
return Resolver.call(logicalClass, member, args);
|
||||
}
|
||||
|
||||
public static Object call(String logicalClass, String member, Object instance,
|
||||
Object... args) {
|
||||
return Resolver.call(logicalClass, member, instance, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflective escape hatch for members the profile does not carry yet.
|
||||
* Mods should prefer the named accessors above.
|
||||
*/
|
||||
public static Field field(Class<?> owner, String name) {
|
||||
try {
|
||||
Field f = owner.getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Method method(Class<?> owner, String name, Class<?>... args) {
|
||||
try {
|
||||
Method m = owner.getDeclaredMethod(name, args);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- threading
|
||||
/** Run a task on the game thread, on the next tick. */
|
||||
public static void onGameThread(Runnable task) {
|
||||
ModBridge.post(task);
|
||||
}
|
||||
|
||||
/** Run a task repeatedly on the game thread. */
|
||||
public static void every(long millis, final Runnable task) {
|
||||
ModBridge.schedule(millis, task);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ logs
|
||||
public static void info(String msg) {
|
||||
rsx.Log.info(msg);
|
||||
}
|
||||
|
||||
public static void warn(String msg) {
|
||||
rsx.Log.warn(msg);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user