Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Quilt Loader and QSL/QFAPI #988

Draft
wants to merge 2 commits into
base: llama
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package me.modmuss50.optifabric.compat.qslscreenext.mixin;

import me.modmuss50.optifabric.compat.InterceptingMixin;
import me.modmuss50.optifabric.compat.PlacatingSurrogate;
import me.modmuss50.optifabric.compat.Shim;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.Window;
import net.minecraft.client.util.math.MatrixStack;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(GameRenderer.class)
@InterceptingMixin("org/quiltmc/qsl/screen/mixin/client/GameRendererMixin")
abstract class GameRendererMixin {
@Shim
private native void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack modelView, MatrixStack matrices);

@PlacatingSurrogate
private void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack matrices) {
}

@Inject(method = "render(FJZ)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", ordinal = 0),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true)
private void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack modelView, MatrixStack matrices) {
onBeforeRenderScreen(tickDelta, startTime, tick, call, mouseX, mouseY, window, projection, modelView, matrices);
}

@Shim
private native void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack modelView, MatrixStack matrices);

@PlacatingSurrogate
private void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack matrices) {
}

@Inject(method = "render(FJZ)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", shift = Shift.AFTER, ordinal = 0),
locals = LocalCapture.CAPTURE_FAILHARD)
private void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack modelView, MatrixStack matrices) {
onAfterRenderScreen(tickDelta, startTime, tick, call, mouseX, mouseY, window, projection, modelView, matrices);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package me.modmuss50.optifabric.compat.qslscreenext.mixin;

import me.modmuss50.optifabric.compat.InterceptingMixin;
import me.modmuss50.optifabric.compat.PlacatingSurrogate;
import me.modmuss50.optifabric.compat.Shim;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.Window;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(GameRenderer.class)
@InterceptingMixin("org/quiltmc/qsl/screen/mixin/client/GameRendererMixin")
abstract class GameRendererOldMixin {
@Shim
private native void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack modelView, MatrixStack matrices);

@PlacatingSurrogate
private void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack matrices) {
}

@Inject(method = "render(FJZ)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", ordinal = 0),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true)
private void onBeforeRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack modelView, MatrixStack matrices) {
onBeforeRenderScreen(tickDelta, startTime, tick, call, mouseX, mouseY, window, projection, modelView, matrices);
}

@Shim
private native void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, Matrix4f projection, MatrixStack modelView, MatrixStack matrices);

@PlacatingSurrogate
private void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack matrices) {
}

@Inject(method = "render(FJZ)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(Lnet/minecraft/client/util/math/MatrixStack;IIF)V", shift = Shift.AFTER, ordinal = 0),
locals = LocalCapture.CAPTURE_FAILHARD)
private void onAfterRenderScreen(float tickDelta, long startTime, boolean tick, CallbackInfo call, int mouseX, int mouseY, Window window, float guiFarPlane, Matrix4f projection, MatrixStack modelView, MatrixStack matrices) {
onAfterRenderScreen(tickDelta, startTime, tick, call, mouseX, mouseY, window, projection, modelView, matrices);
}
}
54 changes: 54 additions & 0 deletions src/main/java/me/modmuss50/optifabric/mod/Loader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package me.modmuss50.optifabric.mod;

import com.google.common.base.MoreObjects;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.VersionParsingException;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.version.VersionPredicate;
import net.fabricmc.loader.util.version.SemanticVersionImpl;
import net.fabricmc.loader.util.version.SemanticVersionPredicateParser;

import java.util.function.BiFunction;
import java.util.function.Predicate;

//Might need this class for other stuff too in the future
public class Loader {
private static final boolean quilt;
private static final BiFunction<String, ModMetadata, Boolean> versionCompareFunction;

static {
quilt = FabricLoader.getInstance().isModLoaded("quilt_loader");
if (quilt) {
versionCompareFunction = (versionRange, mod) -> {
try {
return VersionPredicate.parse(versionRange).test(mod.getVersion());
} catch (VersionParsingException e) {
System.err.println("Error comparing the version for ".concat(MoreObjects.firstNonNull(mod.getName(), mod.getId())));
e.printStackTrace();
return false; //Let's just gamble on the version not being valid also not being a problem
}
};
} else {
versionCompareFunction = (versionRange, mod) -> {
try {
Predicate<SemanticVersionImpl> predicate = SemanticVersionPredicateParser.create(versionRange);
SemanticVersionImpl version = new SemanticVersionImpl(mod.getVersion().getFriendlyString(), false);
return predicate.test(version);
} catch (@SuppressWarnings("deprecation") net.fabricmc.loader.util.version.VersionParsingException e) {
System.err.println("Error comparing the version for ".concat(MoreObjects.firstNonNull(mod.getName(), mod.getId())));
e.printStackTrace();
return false; //Let's just gamble on the version not being valid also not being a problem
}
};
}
}

public static boolean isQuilt() {
return quilt;
}

public static boolean compareVersions(String versionRange, ModMetadata mod) {
return versionCompareFunction.apply(versionRange, mod);
}
}

40 changes: 16 additions & 24 deletions src/main/java/me/modmuss50/optifabric/mod/OptifabricSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import java.util.function.Predicate;
import java.util.regex.Pattern;

import com.google.common.base.MoreObjects;

import org.apache.commons.lang3.tuple.Pair;

import org.objectweb.asm.ClassWriter;
Expand All @@ -25,8 +23,6 @@
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.util.version.SemanticVersionImpl;
import net.fabricmc.loader.util.version.SemanticVersionPredicateParser;

import me.modmuss50.optifabric.mod.OptifineVersion.JarType;
import me.modmuss50.optifabric.patcher.ClassCache;
Expand Down Expand Up @@ -127,15 +123,17 @@ protected boolean isPresent() {
}
}

if (isPresent("fabric-rendering-v1", ">=1.5.0") && particlesPresent.getAsBoolean()) {
if ((isPresent("fabric-rendering-v1", ">=1.5.0") || isPresent("quilted_fabric_rendering_v1")) && particlesPresent.getAsBoolean()) {
if (isPresent("minecraft", ">=1.19.3")) {
Mixins.addConfiguration("optifabric.compat.fabric-rendering.new-mixins.json");
} else {
Mixins.addConfiguration("optifabric.compat.fabric-rendering.mixins.json");
}
}
if (isPresent("fabric-rendering-v1", ">=1.13.0 <2.0") || isPresent("fabric-rendering-v1", ">=2.1.0")) {
Mixins.addConfiguration("optifabric.compat.fabric-rendering.extra-mixins.json");
if (!Loader.isQuilt()) {
Mixins.addConfiguration("optifabric.compat.fabric-rendering.extra-mixins.json");
}
}

if (isPresent("fabric-rendering-data-attachment-v1")) {
Expand Down Expand Up @@ -185,7 +183,7 @@ protected boolean isPresent() {
Mixins.addConfiguration("optifabric.compat.indigo.new-mixins.json");
});
} else {
if (isPresent("fabric-renderer-indigo", ">=0.5.0")) {//First released in 0.51.0+1.18.2
if (isPresent("fabric-renderer-indigo", ">=0.5.0") || isPresent("quilted_fabric_renderer_indigo")) {//First released in 0.51.0+1.18.2
Mixins.addConfiguration("optifabric.compat.indigo.mixins.json");
} else {
Mixins.addConfiguration("optifabric.compat.indigo.old-mixins.json");
Expand All @@ -210,7 +208,13 @@ protected boolean isPresent() {
}

if (isPresent("fabric-screen-api-v1")) {
if (isPresent("minecraft", ">=1.20")) {
if (isPresent("quilt_screen")) {
if (isPresent("minecraft", ">=1.19.3")) {
Mixins.addConfiguration("optifabric.compat.qslscreenext.mixins.json");
} else {
Mixins.addConfiguration("optifabric.compat.qslscreenext.old-mixins.json");
}
} else if (isPresent("minecraft", ">=1.20")) {
Mixins.addConfiguration("optifabric.compat.fabric-screen-api.new4er-mixins.json");
} else if (isPresent("fabric-api", ">=0.81.0")) {
Mixins.addConfiguration("optifabric.compat.fabric-screen-api.new3er-mixins.json");
Expand All @@ -230,7 +234,7 @@ protected boolean isPresent() {

if (isPresent("fabric-lifecycle-events-v1", ">=1.4.6") && isPresent("minecraft", "1.17.x")) {
Mixins.addConfiguration("optifabric.compat.fabric-lifecycle-events.mixins.json");
} else if (isPresent("fabric-lifecycle-events-v1", ">=2.0.8")) {
} else if (isPresent("fabric-lifecycle-events-v1", ">=2.0.8") || isPresent("quilted_fabric_lifecycle_events_v1")) {
Mixins.addConfiguration("optifabric.compat.fabric-lifecycle-events.new-mixins.json");
}

Expand Down Expand Up @@ -268,8 +272,8 @@ protected boolean isPresent() {
Mixins.addConfiguration("optifabric.compat.now-playing.mixins.json");
}

if (isPresent("origins", mod -> compareVersions(Pattern.compile("^1\\.16(\\.\\d)?-").matcher(mod.getVersion().getFriendlyString()).find() ? ">=1.16-0.2.0" : ">=0.4.1 <1.0", mod))) {
if (isPresent("origins", mod -> !Pattern.compile("^1\\.16(\\.\\d)?-").matcher(mod.getVersion().getFriendlyString()).find() || compareVersions(">=1.16.3-0.4.0", mod))) {
if (isPresent("origins", mod -> Loader.compareVersions(Pattern.compile("^1\\.16(\\.\\d)?-").matcher(mod.getVersion().getFriendlyString()).find() ? ">=1.16-0.2.0" : ">=0.4.1 <1.0", mod))) {
if (isPresent("origins", mod -> !Pattern.compile("^1\\.16(\\.\\d)?-").matcher(mod.getVersion().getFriendlyString()).find() || Loader.compareVersions(">=1.16.3-0.4.0", mod))) {
Mixins.addConfiguration("optifabric.compat.origins.mixins.json");
}

Expand Down Expand Up @@ -541,7 +545,7 @@ private static boolean isPresent(String modID) {
}

static boolean isPresent(String modID, String versionRange) {
return isPresent(modID, modMetadata -> compareVersions(versionRange, modMetadata));
return isPresent(modID, modMetadata -> Loader.compareVersions(versionRange, modMetadata));
}

private static boolean isPresent(String modID, Predicate<ModMetadata> extraChecks) {
Expand All @@ -554,16 +558,4 @@ private static boolean isPresent(String modID, Predicate<ModMetadata> extraCheck

return extraChecks.test(modMetadata);
}

private static boolean compareVersions(String versionRange, ModMetadata mod) {
try {
Predicate<SemanticVersionImpl> predicate = SemanticVersionPredicateParser.create(versionRange);
SemanticVersionImpl version = new SemanticVersionImpl(mod.getVersion().getFriendlyString(), false);
return predicate.test(version);
} catch (@SuppressWarnings("deprecation") net.fabricmc.loader.util.version.VersionParsingException e) {
System.err.println("Error comparing the version for ".concat(MoreObjects.firstNonNull(mod.getName(), mod.getId())));
e.printStackTrace();
return false; //Let's just gamble on the version not being valid also not being a problem
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package me.modmuss50.optifabric.patcher.fixes;

import com.google.common.collect.MoreCollectors;
import me.modmuss50.optifabric.util.RemappingUtils;
import org.apache.commons.lang3.Validate;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;

import java.util.Arrays;

public class HeldItemRendererFix implements ClassFixer {
/*
Optifine changes multiple ItemStack#isOf calls to instanceof (and some other stuff)
which don't affect vanilla behaviour, but mess up a redirect of the
aforementioned method. As far as I checked, the only behavioural change
Optifine does is introducing a Shader check at the start of the method.
*/
private static final String stitchName = RemappingUtils.getMethodName("class_759", "method_3228", "(Lnet/minecraft/class_742;FFLnet/minecraft/class_1268;FLnet/minecraft/class_1799;FLnet/minecraft/class_4587;Lnet/minecraft/class_4597;I)V");

@Override
public void fix(ClassNode optifine, ClassNode minecraft) {

//Remove the old method
optifine.methods.removeIf(methodNode -> methodNode.name.equals(stitchName));

//Find the vanilla method
MethodNode methodNode = minecraft.methods.stream().filter(node -> node.name.equals(stitchName)).collect(MoreCollectors.onlyElement());
Validate.notNull(methodNode, "old method null");

//Add the check optifine does to the original method
LabelNode label = (LabelNode) Arrays.stream(methodNode.instructions.toArray()).filter(insn -> insn instanceof LabelNode).findFirst().orElseThrow(() -> new IllegalStateException("No LabelNode?"));
InsnList optifineCheck = new InsnList();
optifineCheck.add(new LabelNode());
optifineCheck.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "net/optifine/Config", "isShaders", "()Z"));
optifineCheck.add(new JumpInsnNode(Opcodes.IFEQ, label));
optifineCheck.add(new VarInsnNode(Opcodes.ALOAD, 4));
String desc = "(L" + RemappingUtils.getClassName("class_1268") + ";)Z";
optifineCheck.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "net/optifine/shaders/Shaders", "isSkipRenderHand", desc));
optifineCheck.add(new JumpInsnNode(Opcodes.IFEQ, label));
optifineCheck.add(new InsnNode(Opcodes.RETURN));
methodNode.instructions.insert(optifineCheck);

optifine.methods.add(methodNode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ private OptifineFixer() {
//net/minecraft/client/render/model/json/ModelOverrideList
registerFix("class_806", new ModelOverrideListFix());

//net/minecraft/client/render/item/HeldItemRenderer
registerFix("class_759", new HeldItemRendererFix());

if (FabricLoader.getInstance().isModLoaded("fabric-rendering-fluids-v1")) {
//net/minecraft/client/render/block/FluidRenderer
registerFix("class_775", new FluidRendererFix());
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/optifabric.compat.qslscreenext.mixins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"parent": "optifabric.mixins.json",
"package": "me.modmuss50.optifabric.compat.qslscreenext.mixin",
"plugin": "me.modmuss50.optifabric.compat.InterceptingMixinPlugin",
"mixins": [
"GameRendererMixin"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"parent": "optifabric.mixins.json",
"package": "me.modmuss50.optifabric.compat.qslscreenext.mixin",
"plugin": "me.modmuss50.optifabric.compat.InterceptingMixinPlugin",
"mixins": [
"GameRendererOldMixin"
]
}