Update for 1.19

This commit is contained in:
Johannes Frohnmeyer 2022-06-08 11:18:34 +02:00
parent d69285213d
commit 6af5a0d9cd
Signed by: Johannes
GPG Key ID: E76429612C2929F4
11 changed files with 218 additions and 76 deletions

View File

@ -3,12 +3,10 @@ apply from: "https://jfmods.gitlab.io/scripts/jfmod.gradle"
dependencies {
modRuntimeOnly "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
include modImplementation("io.gitlab.jfronny.libjf:libjf-config-v0:${project.jfapi_version}")
include modImplementation("io.gitlab.jfronny.libjf:libjf-unsafe-v0:${project.jfapi_version}")
include("io.gitlab.jfronny.libjf:libjf-base:${project.jfapi_version}")
modImplementation("io.gitlab.jfronny.libjf:libjf-config-v0:${project.jfapi_version}")
modImplementation("io.gitlab.jfronny.libjf:libjf-unsafe-v0:${project.jfapi_version}")
modRuntimeOnly("io.gitlab.jfronny.libjf:libjf-devutil-v0:${project.jfapi_version}")
modImplementation "com.terraformersmc:modmenu:3.1.0"
include modRuntimeOnly('io.gitlab.jfronny:gson:2.9.0.2022.4.2.19.45.43') // Dependency of LibJF 2.7.0
modImplementation "com.terraformersmc:modmenu:4.0.0-beta.4"
}

View File

@ -1,14 +1,16 @@
# https://fabricmc.net/develop/
minecraft_version=1.18.2
yarn_mappings=build.2
loader_version=0.13.3
minecraft_version=1.19
yarn_mappings=build.1
loader_version=0.14.6
maven_group=io.jfronny.gitlab
archives_base_name=quickmeth
fabric_version=0.48.0+1.18.2
jfapi_version=2.7.1
fabric_version=0.55.1+1.19
jfapi_version=2.9.1
modrinth_id=hRVfXPJj
modrinth_required_dependencies=WKwQAwke
modrinth_optional_dependencies=mOgUt4GM
curseforge_id=400837
curseforge_required_dependencies=libjf
curseforge_optional_dependencies=modmenu

View File

@ -4,25 +4,37 @@ import io.gitlab.jfronny.libjf.unsafe.asm.AsmConfig;
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.PatchUtil;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import java.util.Map;
import java.util.Set;
public class BytecodeTransformer implements AsmConfig, Patch {
public class BytecodeTransformer implements AsmConfig {
private static final String math = "java/lang/Math";
private static final String random = "java/util/Random";
private static final String mathUtil = "io/gitlab/jfronny/quickmeth/MathUtil";
private static final String mathHelperIntermediary = "net.minecraft.class_3532";
private static final String mathHelper = PatchUtil.getRemappedInternal(mathHelperIntermediary);
private static final Map<String, String> mth = Map.of(
private static final String mojangRandomIntermediary = "net.minecraft.class_5819";
private static final String mojangRandom = PatchUtil.getRemappedInternal(mojangRandomIntermediary);
private static final String mathHelperRandomUuid = mth("method_15378", "(Lnet/minecraft/class_5819;)Ljava/util/UUID;");
private static final Map<String, String> mth = DMap.of( // Maps methods in mathHelper to quickmeth MathUtil methods
mth("method_15374", "(F)F"), "sinM",
mth("method_15362", "(F)F"), "cosM",
mth("method_15355", "(F)F"), "sqrtM"
//mth("method_15375", "(D)I"), "floor"
mth("method_15355", "(F)F"), "sqrtM",
mth("method_15375", "(D)I"), "floor"
);
private static final Map<String, Boolean> stat = Map.of(
private static final Map<String, String> rnd = DMap.of( // Maps methods in minecrafts Random to quickmeth MathUtil methods
rnd("method_43054", "()I"), "nextInt",
rnd("method_43048", "(I)I"), "nextInt",
rnd("method_43055", "()J"), "nextLong",
rnd("method_43056", "()Z"), "nextBoolean",
rnd("method_43057", "()F"), "nextFloat",
rnd("method_43058", "()D"), "random",
rnd("method_43059", "()D"), "random"
);
private static final Map<String, Boolean> stat = Map.of( // Maps quickmeth MathUtil methods to booleans representing whether to overwrite them
"sin", Cfg.corruptTrigonometry2,
"cos", Cfg.corruptTrigonometry2,
"sinM", Cfg.corruptTrigonometry,
@ -30,6 +42,10 @@ public class BytecodeTransformer implements AsmConfig, Patch {
//"sqrt", Cfg.corruptGenericMath,
"sqrtM", Cfg.corruptGenericMath,
//"floor", Cfg.corruptGenericMath2,
// "nextInt", Cfg.corruptGenericMath2,
"nextLong", Cfg.corruptGenericMath2,
"nextBoolean", Cfg.corruptGenericMath2,
// "nextFloat", Cfg.corruptGenericMath2,
"random", Cfg.corruptGenericMath2
);
@ -37,6 +53,10 @@ public class BytecodeTransformer implements AsmConfig, Patch {
return AsmTransformer.MAPPING_RESOLVER.mapMethodName(AsmTransformer.INTERMEDIARY, mathHelperIntermediary, method, descriptor);
}
private static String rnd(String method, String descriptor) {
return AsmTransformer.MAPPING_RESOLVER.mapMethodName(AsmTransformer.INTERMEDIARY, mojangRandomIntermediary, method, descriptor);
}
@Override
public Set<String> skipClasses() {
return null;
@ -44,45 +64,73 @@ public class BytecodeTransformer implements AsmConfig, Patch {
@Override
public Set<Patch> getPatches() {
return Set.of(this);
return Set.of(this::patchInvokes);
}
@Override
public void apply(ClassNode klazz) {
private void patchInvokes(ClassNode klazz) {
for (MethodNode method : klazz.methods) {
if (klazz.name.equals(mathHelper) && method.name.equals(mathHelperRandomUuid)) { // UUIDs still need to work
if (Cfg.debugAsm) {
ModMain.LOGGER.info("Skipped replacing method calls in MathHelper.randomUuid");
}
continue;
}
for (AbstractInsnNode insn : method.instructions.toArray()) {
if (insn.getOpcode() == Opcodes.INVOKESTATIC || insn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
if (insn.getOpcode() == Opcodes.INVOKESTATIC || insn.getOpcode() == Opcodes.INVOKEVIRTUAL || insn.getOpcode() == Opcodes.INVOKEINTERFACE) {
String insNew = null;
MethodInsnNode mIns = (MethodInsnNode) insn;
// Resolve a possible replacement method in quickmeth MathUtil
if (mIns.owner.equals(math)) {
if (stat.containsKey(mIns.name) && stat.get(mIns.name))
if (stat.containsKey(mIns.name))
insNew = mIns.name;
}
else if (mIns.owner.equals(mathHelper)) {
if (mth.containsKey(mIns.name)) {
} else if (mIns.owner.equals(mathHelper)) {
if (mth.containsKey(mIns.name))
insNew = mth.get(mIns.name);
if (!stat.get(insNew))
insNew = null;
}
} else if (mIns.owner.equals(mojangRandom)) {
if (rnd.containsKey(mIns.name))
insNew = rnd.get(mIns.name);
} else if (mIns.owner.equals(random)) {
insNew = switch (mIns.name) {
case "nextInt" -> "nextInt";
case "nextLong" -> "nextLong";
case "nextBoolean" -> "nextBoolean";
case "nextFloat" -> "nextFloat";
case "nextDouble", "nextGaussian" -> "random";
default -> null;
};
}
else if (mIns.owner.equals(random)) {
if (Cfg.corruptGenericMath2 && !klazz.name.equals(mathUtil)) {
insNew = switch (mIns.name) {
//case "nextInt" -> "nextInt";
case "nextLong" -> "nextLong";
case "nextBoolean" -> "nextBoolean";
//case "nextFloat" -> "nextFloat";
case "nextDouble", "nextGaussian" -> "random";
default -> null;
};
}
}
if (insNew != null) {
if (mIns.getOpcode() == Opcodes.INVOKEVIRTUAL)
// Check whether the method should be replaced
if (!klazz.name.equals(mathUtil) && insNew != null && stat.containsKey(insNew) && stat.get(insNew)) {
String originalOwner = mIns.owner;
String originalName = mIns.name;
// Pop the instance when calling an instance method
if (mIns.getOpcode() != Opcodes.INVOKESTATIC) {
Type[] params = Type.getArgumentTypes(mIns.desc);
// This implementation only works with 0 or 1 parameters of category 1 computational types
// This means that doubles and longs are unsupported
if (params.length > 1)
throw new IllegalArgumentException("The quickmeth bytecode transformer does not support more than one argument");
for (Type param : params) {
if (param.getSize() != 1)
throw new IllegalStateException("The quickmeth bytecode transformer only supports category 1 computational types");
}
// If a parameter is present, swap the object to the top, then pop
if (params.length == 1)
method.instructions.insertBefore(mIns, new InsnNode(Opcodes.SWAP));
// Pop the object instance, leaving the parameter if it exists
method.instructions.insertBefore(mIns, new InsnNode(Opcodes.POP));
}
// Invoke the static method
mIns.setOpcode(Opcodes.INVOKESTATIC);
mIns.owner = mathUtil;
mIns.name = insNew;
mIns.itf = false;
if (Cfg.debugAsm) {
ModMain.LOGGER.info("Replaced call to L" + originalOwner + ";" + originalName + mIns.desc
+ " in " + klazz.name
+ " with L" + mIns.owner + ";" + mIns.name + mIns.desc);
}
}
}
}

View File

@ -16,4 +16,6 @@ public class Cfg implements JfConfig {
public static Boolean corruptPerlinNoise = true;
@Entry
public static Boolean corruptSimplexNoise = true;
@Entry
public static Boolean debugAsm = false;
}

View File

@ -0,0 +1,75 @@
package io.gitlab.jfronny.quickmeth;
import java.util.*;
/** Creates a map that deduplicates keys using the Map.of() syntax */
public class DMap {
static <K, V> Map<K, V> of() {
return Map.of();
}
static <K, V> Map<K, V> of(K k1, V v1) {
return Map.of(k1, v1);
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
return createMulti(e(k1, v1), e(k2, v2));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5), e(k6, v6));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5), e(k6, v6), e(k7, v7));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5), e(k6, v6), e(k7, v7), e(k8, v8));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5), e(k6, v6), e(k7, v7), e(k8, v8), e(k9, v9));
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
return createMulti(e(k1, v1), e(k2, v2), e(k3, v3), e(k4, v4), e(k5, v5), e(k6, v6), e(k7, v7), e(k8, v8), e(k9, v9), e(k10, v10));
}
private static <K, V> Map.Entry<K, V> e(K key, V value) {
return new Map.Entry<>() {
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V v) {
throw new UnsupportedOperationException();
}
};
}
private static <K, V> Map<K, V> createMulti(Map.Entry<K, V>... entries) {
Map<K, V> map = new HashMap<>();
for (Map.Entry<K, V> entry : entries) map.put(entry.getKey(), entry.getValue());
return Map.copyOf(map);
}
}

View File

@ -77,7 +77,7 @@ public class MathUtil {
return 2;
}
public static int nextInt(int bound) {
return Math.min(2, bound);
return Math.min(2, bound - 1);
}
public static long nextLong() {
return 2;

View File

@ -1,11 +1,10 @@
package io.gitlab.jfronny.quickmeth;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.gitlab.jfronny.commons.log.*;
import net.fabricmc.api.*;
public class ModMain implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("quickmäth");
public static final Logger LOGGER = Logger.forName("quickmäth");
@Override
public void onInitialize() {
LOGGER.info("QuickMäth initialized, but why are you using this?");

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.quickmeth.mixin;
import io.gitlab.jfronny.quickmeth.MathUtil;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.random.*;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@ -11,8 +12,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Random;
@Mixin(MathHelper.class)
public abstract class MathHelperMixin {
@Shadow @Final private static Random RANDOM;
@ -45,21 +44,21 @@ public abstract class MathHelperMixin {
return -1 - Math.max(d, e);
}
@ModifyVariable(method = "nextInt(Ljava/util/Random;II)I", at = @At("HEAD"), argsOnly = true, ordinal = 0)
@ModifyVariable(method = "nextInt(Lnet/minecraft/util/math/random/Random;II)I", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private static int adjustRandomDoubleParam(int min) {
return Math.max(min - 1, 0);
}
@Inject(method = "nextFloat(Ljava/util/Random;FF)F", at = @At("TAIL"), cancellable = true)
@Inject(method = "nextFloat(Lnet/minecraft/util/math/random/Random;FF)F", at = @At("TAIL"), cancellable = true)
private static void adjustRandomFloat(CallbackInfoReturnable<Float> ci) {
ci.setReturnValue(ci.getReturnValue() * 0.9f);
}
@ModifyVariable(method = "nextDouble(Ljava/util/Random;DD)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
@ModifyVariable(method = "nextDouble(Lnet/minecraft/util/math/random/Random;DD)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private static double adjustRandomDoubleParam(double min) {
return min - 1;
}
@Inject(method = "nextDouble(Ljava/util/Random;DD)D", at = @At("TAIL"), cancellable = true)
@Inject(method = "nextDouble(Lnet/minecraft/util/math/random/Random;DD)D", at = @At("TAIL"), cancellable = true)
private static void adjustRandomDouble(CallbackInfoReturnable<Double> ci) {
ci.setReturnValue((double) floor(ci.getReturnValue()));
}

View File

@ -10,35 +10,43 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PerlinNoiseSampler.class)
public abstract class MixinPerlinNoiseSampler {
@ModifyVariable(method = "sample(IIIDDDD)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
@ModifyVariable(method = "sample(IIIDDDD)D", at = @At("HEAD"), argsOnly = true, index = 4)
private double sampleAdjustX(double localX) {
return MathUtil.boxedInvert(localX);
}
@ModifyVariable(method = "sampleDerivative(IIIDDD[D)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private double derivAdjustX(double localX) {
return MathUtil.boxedInvert(localX);
@ModifyVariable(method = "sample(IIIDDDD)D", at = @At("HEAD"), argsOnly = true, index = 6)
private double sampleAdjustY(double localY) {
return MathUtil.boxedInvert(localY);
}
//Once again, the y parameter is ignored because mixin
@ModifyVariable(method = "sample(IIIDDDD)D", at = @At("HEAD"), argsOnly = true, ordinal = 2)
@ModifyVariable(method = "sample(IIIDDDD)D", at = @At("HEAD"), argsOnly = true, index = 8)
private double sampleAdjustZ(double localZ) {
return MathUtil.boxedInvert(localZ);
}
@ModifyVariable(method = "sampleDerivative(IIIDDD[D)D", at = @At("HEAD"), argsOnly = true, ordinal = 2)
@Inject(method = "sample(IIIDDDD)D", at = @At("TAIL"), cancellable = true)
private void sampleAdjustResult(CallbackInfoReturnable<Double> ret) {
ret.setReturnValue(MathUtil.boxedInvert(ret.getReturnValue()));
}
@ModifyVariable(method = "sampleDerivative(IIIDDD[D)D", at = @At("HEAD"), argsOnly = true, index = 4)
private double derivAdjustX(double localX) {
return MathUtil.boxedInvert(localX);
}
@ModifyVariable(method = "sampleDerivative(IIIDDD[D)D", at = @At("HEAD"), argsOnly = true, index = 6)
private double derivAdjustY(double localY) {
return MathUtil.boxedInvert(localY);
}
@ModifyVariable(method = "sampleDerivative(IIIDDD[D)D", at = @At("HEAD"), argsOnly = true, index = 8)
private double derivAdjustZ(double localZ) {
return MathUtil.boxedInvert(localZ);
}
@Inject(method = "sample(IIIDDDD)D", at = @At("TAIL"), cancellable = true)
private void sampleAsjustResult(CallbackInfoReturnable<Double> ret) {
ret.setReturnValue(MathUtil.boxedInvert(ret.getReturnValue()));
}
@Inject(method = "sampleDerivative(IIIDDD[D)D", at = @At("TAIL"), cancellable = true)
private void derivAsjustResult(CallbackInfoReturnable<Double> ret) {
private void derivAdjustResult(CallbackInfoReturnable<Double> ret) {
ret.setReturnValue(MathUtil.boxedInvert(ret.getReturnValue()));
}
}

View File

@ -15,23 +15,32 @@ public abstract class MixinSimplexNoiseSampler {
ret.setReturnValue(MathUtil.boxedInvert(ret.getReturnValue()));
}
@ModifyVariable(method = "sample(DD)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
@ModifyVariable(method = "sample(DD)D", at = @At("HEAD"), argsOnly = true, index = 1)
private double sampleAdjustX(double x) {
return MathUtil.boxedInvert(x);
}
// The y parameter is ignored here because otherwise this will crash. I don't know why.
@Inject(method = "getGradient(I)I", at = @At("TAIL"), cancellable = true)
private void gradientAdjust(CallbackInfoReturnable<Integer> ret) {
@ModifyVariable(method = "sample(DD)D", at = @At("HEAD"), argsOnly = true, index = 3)
private double sampleAdjustY(double y) {
return MathUtil.boxedInvert(y);
}
@Inject(method = "map(I)I", at = @At("TAIL"), cancellable = true)
private void mapAdjust(CallbackInfoReturnable<Integer> ret) {
ret.setReturnValue(ret.getReturnValue() & 255);
}
@ModifyVariable(method = "grad(IDDDD)D", at = @At("HEAD"), argsOnly = true, ordinal = 0)
@ModifyVariable(method = "grad(IDDDD)D", at = @At("HEAD"), argsOnly = true, index = 2)
private double gradAdjustX(double x) {
return MathUtil.boxedInvert(x);
}
// The y parameter is ignored here because otherwise this will crash. I don't know why.
@ModifyVariable(method = "grad(IDDDD)D", at = @At("HEAD"), argsOnly = true, ordinal = 2)
@ModifyVariable(method = "grad(IDDDD)D", at = @At("HEAD"), argsOnly = true, index = 4)
private double gradAdjustY(double y) {
return MathUtil.boxedInvert(y);
}
@ModifyVariable(method = "grad(IDDDD)D", at = @At("HEAD"), argsOnly = true, index = 6)
private double gradAdjustZ(double z) {
return MathUtil.boxedInvert(z);
}

View File

@ -1,5 +1,5 @@
{
"quickmeth.jfconfig.title": "QuickMäth",
"quickmeth.jfconfig.title": "QuickMäth - Restart to apply changes",
"quickmeth.jfconfig.corruptGenericMath": "Corrupt generic math",
"quickmeth.jfconfig.corruptGenericMath.tooltip": "Corrupts methods in MathHelper. This will impact many things but most notably movement",
"quickmeth.jfconfig.corruptGenericMath2": "Corrupt more generic math",
@ -11,5 +11,7 @@
"quickmeth.jfconfig.corruptPerlinNoise": "Corrupt perlin noise",
"quickmeth.jfconfig.corruptPerlinNoise.tooltip": "Corrupts methods in OctavePerlinNoiseSampler and PerlinNoiseSampler. This will mostly impact world generation",
"quickmeth.jfconfig.corruptSimplexNoise": "Corrupt simplex noise",
"quickmeth.jfconfig.corruptSimplexNoise.tooltip": "Corrupts methods in SimplexNoiseSampler. I am actually not sure what this breaks"
"quickmeth.jfconfig.corruptSimplexNoise.tooltip": "Corrupts methods in SimplexNoiseSampler. I am actually not sure what this breaks",
"quickmeth.jfconfig.debugAsm": "Debug ASM",
"quickmeth.jfconfig.debugAsm.tooltip": "Prints debug messages for the purpose of debugging"
}