diff options
Diffstat (limited to 'src/main/java/moe/nea')
87 files changed, 2108 insertions, 845 deletions
diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt new file mode 100644 index 0000000..1e204d6 --- /dev/null +++ b/src/main/java/moe/nea/firmament/gui/config/storage/ArrayIndexedJsonPointer.kt @@ -0,0 +1,17 @@ +package moe.nea.firmament.gui.config.storage + +import com.google.gson.JsonArray +import com.google.gson.JsonElement + +data class ArrayIndexedJsonPointer( + val owner: JsonArray, + val index: Int +) : JsonPointer { + override fun get(): JsonElement { + return owner.get(index) + } + + override fun set(value: JsonElement) { + owner.set(index, value) + } +} diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt new file mode 100644 index 0000000..df1ed33 --- /dev/null +++ b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigEditor.kt @@ -0,0 +1,104 @@ +package moe.nea.firmament.gui.config.storage + +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import kotlinx.serialization.json.JsonElement +import moe.nea.firmament.util.json.intoGson +import moe.nea.firmament.util.json.intoKotlinJson + +data class ConfigEditor( + val roots: List<JsonPointer>, +) { + fun transform(transform: (JsonElement) -> JsonElement) { + roots.forEach { root -> + root.set(transform(root.get().intoKotlinJson()).intoGson()) + } + } + + fun move(fromPath: String, toPath: String) { + if (fromPath == toPath) return + val fromSegments = fromPath.split(".").filter { it.isNotEmpty() } + val toSegments = toPath.split(".").filter { it.isNotEmpty() } + roots.forEach { root -> + var fp = root.get() + if (fromSegments.isEmpty()) { + root.set(JsonObject()) + } else { + fromSegments.dropLast(1).forEach { + fp = (fp as JsonObject)[it] ?: return@forEach // todo warn if we dont find the object maybe + } + fp as JsonObject + fp = fp.remove(fromSegments.last())?.deepCopy() ?: return@forEach // in theory i don't need to deepcopy but fuck theory + } + if (toSegments.isEmpty()) { + root.set(fp) + } else { + var lp = root.get() + toSegments.dropLast(1).forEach { name -> + val parent = lp as JsonObject + var child = parent[name] + if (child == null) { + child = JsonObject() + parent.add(name, child) + } + lp = child + } + lp as JsonObject + if (lp.has(toSegments.last())) { + error("Cannot overwrite $lp.${toSegments.last()} with $fp") + } + lp.add(toSegments.last(), fp) + } + } + } + + fun at(path: String, block: ConfigEditor.() -> Unit) { + block(at(path)) + } + + fun at(path: String): ConfigEditor { + var lastRoots = roots + for (segment in path.split(".")) { + if (segment.isEmpty()) { + continue + } else if (segment == "*") { + lastRoots = lastRoots.flatMap { root -> + when (val ele = root.get()) { + is JsonObject -> { + ele.entrySet().map { + (ObjectIndexedJsonPointer(ele, it.key)) + } + } + + is JsonArray -> { + (0..<ele.size()).map { + (ArrayIndexedJsonPointer(ele, it)) + } + } + + else -> { + error("Cannot expand a json primitive $ele at $path") + } + } + } + } else { + lastRoots = lastRoots.map { root -> + when (val ele = root.get()) { + is JsonObject -> { + ObjectIndexedJsonPointer(ele, segment) + } + + is JsonArray -> { + ArrayIndexedJsonPointer(ele, segment.toInt()) + } + + else -> { + error("Cannot expand a json primitive $ele at $path") + } + } + } + } + } + return ConfigEditor(lastRoots) + } +} diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt new file mode 100644 index 0000000..07148d5 --- /dev/null +++ b/src/main/java/moe/nea/firmament/gui/config/storage/ConfigFixEvent.kt @@ -0,0 +1,38 @@ +package moe.nea.firmament.gui.config.storage + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import moe.nea.firmament.events.FirmamentEvent +import moe.nea.firmament.events.FirmamentEventBus + +data class ConfigFixEvent( + val storageClass: ConfigStorageClass, + val toVersion: Int, + var data: JsonObject, +) : FirmamentEvent() { + companion object : FirmamentEventBus<ConfigFixEvent>() { + + } + fun on( + toVersion: Int, + storageClass: ConfigStorageClass, + block: ConfigEditor.() -> Unit + ) { + require(toVersion <= FirmamentConfigLoader.currentConfigVersion) + if (this.toVersion == toVersion && this.storageClass == storageClass) { + block(ConfigEditor(listOf(object : JsonPointer { + override fun get(): JsonObject { + return data + } + + override fun set(value: JsonElement) { + data = value as JsonObject + } + + override fun toString(): String { + return "ConfigRoot($storageClass)" + } + }))) + } + } +} diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt new file mode 100644 index 0000000..e34c312 --- /dev/null +++ b/src/main/java/moe/nea/firmament/gui/config/storage/JsonPointer.kt @@ -0,0 +1,8 @@ +package moe.nea.firmament.gui.config.storage + +import com.google.gson.JsonElement + +interface JsonPointer { + fun get(): JsonElement + fun set(value: JsonElement) +} diff --git a/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt b/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt new file mode 100644 index 0000000..091275d --- /dev/null +++ b/src/main/java/moe/nea/firmament/gui/config/storage/ObjectIndexedJsonPointer.kt @@ -0,0 +1,17 @@ +package moe.nea.firmament.gui.config.storage + +import com.google.gson.JsonElement +import com.google.gson.JsonObject + +data class ObjectIndexedJsonPointer( + val owner: JsonObject, + val name: String +) : JsonPointer { + override fun get(): JsonElement { + return owner.get(name) + } + + override fun set(value: JsonElement) { + owner.add(name, value) + } +} diff --git a/src/main/java/moe/nea/firmament/init/AutoDiscoveryPlugin.java b/src/main/java/moe/nea/firmament/init/AutoDiscoveryPlugin.java index 0713068..07e4549 100644 --- a/src/main/java/moe/nea/firmament/init/AutoDiscoveryPlugin.java +++ b/src/main/java/moe/nea/firmament/init/AutoDiscoveryPlugin.java @@ -1,6 +1,9 @@ package moe.nea.firmament.init; +import moe.nea.firmament.util.ErrorUtil; +import moe.nea.firmament.util.compatloader.ICompatMeta; + import java.io.File; import java.io.IOException; import java.net.MalformedURLException; @@ -24,6 +27,8 @@ public class AutoDiscoveryPlugin { return mixins.stream().map(it -> defaultName + "." + it).toList(); } + // TODO: remove println + private static final List<AutoDiscoveryPlugin> mixinPlugins = new ArrayList<>(); public static List<AutoDiscoveryPlugin> getMixinPlugins() { @@ -94,7 +99,7 @@ public class AutoDiscoveryPlugin { String norm = (className.substring(0, className.length() - ".class".length())) .replace("\\", "/") .replace("/", "."); - if (norm.startsWith(getMixinPackage() + ".") && !norm.endsWith(".")) { + if (norm.startsWith(getMixinPackage() + ".") && !norm.endsWith(".") && ICompatMeta.Companion.shouldLoad(norm)) { mixins.add(norm.substring(getMixinPackage().length() + 1)); } } @@ -125,24 +130,25 @@ public class AutoDiscoveryPlugin { */ public List<String> getMixins() { if (mixins != null) return mixins; - System.out.println("Trying to discover mixins"); - mixins = new ArrayList<>(); - URL classUrl = getClass().getProtectionDomain().getCodeSource().getLocation(); - System.out.println("Found classes at " + classUrl); - tryDiscoverFromContentFile(classUrl); - var classRoots = System.getProperty("firmament.classroots"); - if (classRoots != null && !classRoots.isBlank()) { - System.out.println("Found firmament class roots: " + classRoots); - for (String s : classRoots.split(File.pathSeparator)) { - if (s.isBlank()) { - continue; - } - try { + try { + System.out.println("Trying to discover mixins"); + mixins = new ArrayList<>(); + URL classUrl = getClass().getProtectionDomain().getCodeSource().getLocation(); + System.out.println("Found classes at " + classUrl); + tryDiscoverFromContentFile(classUrl); + var classRoots = System.getProperty("firmament.classroots"); + if (classRoots != null && !classRoots.isBlank()) { + System.out.println("Found firmament class roots: " + classRoots); + for (String s : classRoots.split(File.pathSeparator)) { + if (s.isBlank()) { + continue; + } tryDiscoverFromContentFile(new File(s).toURI().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); } } + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); } return mixins; } diff --git a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java b/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java deleted file mode 100644 index d60e3e7..0000000 --- a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java +++ /dev/null @@ -1,75 +0,0 @@ -package moe.nea.firmament.init; - -import me.shedaniel.mm.api.ClassTinkerers; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; - -import java.lang.reflect.Modifier; -import java.util.Objects; - -public class ClientPlayerRiser extends RiserUtils { - @IntermediaryName(net.minecraft.entity.player.PlayerEntity.class) - String PlayerEntity; - @IntermediaryName(net.minecraft.world.World.class) - String World; - String GameProfile = "com.mojang.authlib.GameProfile"; - @IntermediaryName(net.minecraft.util.math.BlockPos.class) - String BlockPos; - @IntermediaryName(net.minecraft.client.network.AbstractClientPlayerEntity.class) - String AbstractClientPlayerEntity; - String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer"; - // World world, BlockPos pos, float yaw, GameProfile gameProfile - Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile)); - - - private void mapClassNode(ClassNode classNode, Type superClass) { - for (MethodNode method : classNode.methods) { - if (Objects.equals(method.name, "<init>") && Type.getMethodType(method.desc).equals(constructorDescriptor)) { - modifyConstructor(method, superClass); - return; - } - } - var node = new MethodNode(Opcodes.ASM9, "<init>", constructorDescriptor.getDescriptor(), null, null); - classNode.methods.add(node); - modifyConstructor(node, superClass); - } - - - private void modifyConstructor(MethodNode method, Type superClass) { - method.access = (method.access | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED; - if (method.instructions.size() != 0) return; // Some other mod has already made a constructor here - - // World world, BlockPos pos, float yaw, GameProfile gameProfile - // ALOAD this - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); - - // ALOAD World - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); - - // ALOAD BlockPos - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); - - // ALOAD yaw - method.instructions.add(new VarInsnNode(Opcodes.FLOAD, 3)); - - // ALOAD gameProfile - method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 4)); - - // Call super - method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, superClass.getInternalName(), "<init>", constructorDescriptor.getDescriptor(), false)); - - // Return - method.instructions.add(new InsnNode(Opcodes.RETURN)); - } - - @Override - public void addTinkerers() { - ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity)), true); - ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity)), true); - } -} diff --git a/src/main/java/moe/nea/firmament/init/EarlyRiser.java b/src/main/java/moe/nea/firmament/init/EarlyRiser.java index 5441255..ae26bd7 100644 --- a/src/main/java/moe/nea/firmament/init/EarlyRiser.java +++ b/src/main/java/moe/nea/firmament/init/EarlyRiser.java @@ -4,7 +4,6 @@ package moe.nea.firmament.init; public class EarlyRiser implements Runnable { @Override public void run() { - new ClientPlayerRiser().addTinkerers(); new HandledScreenRiser().addTinkerers(); new SectionBuilderRiser().addTinkerers(); // TODO: new ItemColorsSodiumRiser().addTinkerers(); diff --git a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java index f7db18c..98be517 100644 --- a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java +++ b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java @@ -2,7 +2,13 @@ package moe.nea.firmament.init; import me.shedaniel.mm.api.ClassTinkerers; -import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; @@ -19,30 +25,46 @@ import java.lang.reflect.Modifier; import java.util.function.Consumer; public class HandledScreenRiser extends RiserUtils { - @IntermediaryName(net.minecraft.client.gui.screen.Screen.class) - String Screen; - @IntermediaryName(net.minecraft.client.gui.screen.ingame.HandledScreen.class) - String HandledScreen; - Type mouseScrolledDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE); - String mouseScrolled = remapper.mapMethodName("intermediary", "net.minecraft.class_364", "method_25401", - mouseScrolledDesc.getDescriptor()); - // boolean keyReleased(int keyCode, int scanCode, int modifiers) - Type keyReleasedDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE); - String keyReleased = remapper.mapMethodName("intermediary", Intermediary.<Element>className(), - Intermediary.methodName(Element::keyReleased), - keyReleasedDesc.getDescriptor()); - // public boolean charTyped(char chr, int modifiers) - Type charTypedDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.INT_TYPE); - String charTyped = remapper.mapMethodName("intermediary", Intermediary.<Element>className(), - Intermediary.methodName(Element::charTyped), - charTypedDesc.getDescriptor()); + Intermediary.InterClass Screen = Intermediary.<Screen>intermediaryClass(); + Intermediary.InterClass KeyInput = Intermediary.<KeyEvent>intermediaryClass(); + Intermediary.InterClass CharInput = Intermediary.<CharacterEvent>intermediaryClass(); + Intermediary.InterClass HandledScreen = Intermediary.<AbstractContainerScreen>intermediaryClass(); + Intermediary.InterClass AbstractContainerEventHandler = Intermediary.<AbstractContainerEventHandler>intermediaryClass(); + Intermediary.InterClass MouseButtonEvent = Intermediary.<MouseButtonEvent>intermediaryClass(); + Intermediary.InterMethod mouseScrolled = Intermediary.intermediaryMethod( + GuiEventListener::mouseScrolled, + Intermediary.ofClass(boolean.class), + Intermediary.ofClass(double.class), + Intermediary.ofClass(double.class), + Intermediary.ofClass(double.class), + Intermediary.ofClass(double.class) + ); + Intermediary.InterMethod mouseClickedScreen = Intermediary.intermediaryMethod( + //onMouseClicked$firmament + GuiEventListener::mouseClicked, + Intermediary.ofClass(boolean.class), + MouseButtonEvent, + Intermediary.ofClass(boolean.class) + ); + ; + Intermediary.InterMethod keyReleased = Intermediary.intermediaryMethod( + GuiEventListener::keyReleased, + Intermediary.ofClass(boolean.class), + KeyInput + ); + Intermediary.InterMethod charTyped = Intermediary.intermediaryMethod( + GuiEventListener::charTyped, + Intermediary.ofClass(boolean.class), + CharInput + ); @Override public void addTinkerers() { - ClassTinkerers.addTransformation(HandledScreen, this::addMouseScroll, true); - ClassTinkerers.addTransformation(HandledScreen, this::addKeyReleased, true); - ClassTinkerers.addTransformation(HandledScreen, this::addCharTyped, true); + addTransformation(HandledScreen, this::addMouseScroll, true); + addTransformation(HandledScreen, this::addKeyReleased, true); + addTransformation(HandledScreen, this::addCharTyped, true); + addTransformation(Screen, this::addMouseClicked, true); } /** @@ -56,8 +78,8 @@ public class HandledScreenRiser extends RiserUtils { * @param insertInvoke insert the invokevirtual/invokestatic call */ void insertTrueHandler(MethodNode node, - Consumer<InsnList> insertLoads, - Consumer<InsnList> insertInvoke) { + Consumer<InsnList> insertLoads, + Consumer<InsnList> insertInvoke) { var insns = new InsnList(); insertLoads.accept(insns); @@ -76,26 +98,39 @@ public class HandledScreenRiser extends RiserUtils { void addKeyReleased(ClassNode classNode) { addSuperInjector( - classNode, keyReleased, keyReleasedDesc, "keyReleased_firmament", + classNode, keyReleased.mapped(), keyReleased.mappedDesc(), HandledScreen, Screen, "keyReleased_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); - // ILOAD 1-3, load args - insns.add(new VarInsnNode(Opcodes.ILOAD, 1)); - insns.add(new VarInsnNode(Opcodes.ILOAD, 2)); - insns.add(new VarInsnNode(Opcodes.ILOAD, 3)); + // ALOAD 1, load args + insns.add(new VarInsnNode(Opcodes.ALOAD, 1)); }); } + void addMouseClicked(ClassNode classNode) { + addSuperInjector( + classNode, mouseClickedScreen.mapped(), mouseClickedScreen.mappedDesc(), + Screen, AbstractContainerEventHandler, "onMouseClicked$firmament", + insns -> { + // load this + insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + // load mouse event + insns.add(new VarInsnNode(Opcodes.ALOAD, 1)); + // load doubled + insns.add(new VarInsnNode(Opcodes.ILOAD, 2)); + } + ); + } + void addCharTyped(ClassNode classNode) { addSuperInjector( - classNode, charTyped, charTypedDesc, "charTyped_firmament", + classNode, charTyped.mapped(), charTyped.mappedDesc(), + HandledScreen, Screen, "charTyped_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); - // ILOAD 1-2, load args. chars = ints - insns.add(new VarInsnNode(Opcodes.ILOAD, 1)); - insns.add(new VarInsnNode(Opcodes.ILOAD, 2)); + // ALOAD 1, load args + insns.add(new VarInsnNode(Opcodes.ALOAD, 1)); }); } @@ -103,6 +138,8 @@ public class HandledScreenRiser extends RiserUtils { ClassNode classNode, String name, Type desc, + Intermediary.InterClass currentClass, + Intermediary.InterClass parentClass, String firmamentName, Consumer<InsnList> loadArgs ) { @@ -118,8 +155,8 @@ public class HandledScreenRiser extends RiserUtils { var insns = keyReleasedNode.instructions; loadArgs.accept(insns); // INVOKESPECIAL call super method - insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, getTypeForClassName(Screen).getInternalName(), - name, desc.getDescriptor())); + insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, parentClass.mapped().getInternalName(), + name, desc.getDescriptor())); // IRETURN return int on stack (booleans are int at runtime) insns.add(new InsnNode(Opcodes.IRETURN)); classNode.methods.add(keyReleasedNode); @@ -127,16 +164,16 @@ public class HandledScreenRiser extends RiserUtils { insertTrueHandler(keyReleasedNode, loadArgs, insns -> { // INVOKEVIRTUAL call custom handler insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, - getTypeForClassName(HandledScreen).getInternalName(), - firmamentName, - desc.getDescriptor())); + currentClass.mapped().getInternalName(), + firmamentName, + desc.getDescriptor())); }); } void addMouseScroll(ClassNode classNode) { addSuperInjector( - classNode, mouseScrolled, mouseScrolledDesc, "mouseScrolled_firmament", + classNode, mouseScrolled.mapped(), mouseScrolled.mappedDesc(), HandledScreen, Screen, "mouseScrolled_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); diff --git a/src/main/java/moe/nea/firmament/init/Intermediary.java b/src/main/java/moe/nea/firmament/init/Intermediary.java index 61494d7..3513a6b 100644 --- a/src/main/java/moe/nea/firmament/init/Intermediary.java +++ b/src/main/java/moe/nea/firmament/init/Intermediary.java @@ -7,57 +7,66 @@ import org.objectweb.asm.Type; import java.util.List; public class Intermediary { - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - static String methodName(Object object) { - throw new AssertionError("Cannot be called at runtime"); - } - - static <T> String className() { - throw new AssertionError("Cannot be called at runtime"); - } - - static String id(String source) { - return source; - } - -// public record Class( -// Type intermediaryClass -// ) { -// public Class(String intermediaryClass) { -// this(Type.getObjectType(intermediaryClass.replace('.', '/'))); -// } -// -// public String getMappedName() { -// return RESOLVER.mapClassName("intermediary", intermediaryClass.getInternalName() -// .replace('/', '.')); -// } -// } -// -// public record Method( -// Type intermediaryClassName, -// String intermediaryMethodName, -// Type intermediaryReturnType, -// List<Type> intermediaryArgumentTypes -// ) { -// public Method( -// String intermediaryClassName, -// String intermediaryMethodName, -// String intermediaryReturnType, -// String... intermediaryArgumentTypes -// ) { -// this(intermediaryClassName, intermediaryMethodName, intermediaryReturnType, List.of(intermediaryArgumentTypes)); -// } -// -// public String getMappedMethodName() { -// return RESOLVER.mapMethodName("intermediary", -// intermediaryClassName.getInternalName().replace('/', '.')); -// } -// -// public Type getIntermediaryDescriptor() { -// return Type.getMethodType(intermediaryReturnType, intermediaryArgumentTypes.toArray(Type[]::new)); -// } -// -// -// } + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + static InterMethod intermediaryMethod(Object object, InterClass returnType, InterClass... args) { + throw new AssertionError("Cannot be called at runtime"); + } + + static <T> InterClass intermediaryClass() { + throw new AssertionError("Cannot be called at runtime"); + } + + public static InterClass ofIntermediaryClass(String interClass) { + return new InterClass(Type.getObjectType(interClass.replace('.', '/'))); + } + + public static InterClass ofClass(Class<?> unmappedClass) { + return new InterClass(Type.getType(unmappedClass)); + } + + public static InterMethod ofMethod(String intermediary, String ownerType, InterClass returnType, InterClass... argTypes) { + return new InterMethod(intermediary, ofIntermediaryClass(ownerType), returnType, List.of(argTypes)); + } + + public record InterClass( + Type intermediary + ) { + public Type mapped() { + if (intermediary().getSort() != Type.OBJECT) + return intermediary(); + return Type.getObjectType(RESOLVER.mapClassName("intermediary", intermediary().getClassName()) + .replace('.', '/')); + } + } + + public record InterMethod( + String intermediary, + InterClass ownerType, + InterClass returnType, + List<InterClass> argumentTypes + ) { + public Type intermediaryDesc() { + return Type.getMethodType( + returnType.intermediary(), + argumentTypes().stream().map(InterClass::intermediary).toArray(Type[]::new) + ); + } + + public Type mappedDesc() { + return Type.getMethodType( + returnType.mapped(), + argumentTypes().stream().map(InterClass::mapped).toArray(Type[]::new) + ); + } + + public String mapped() { + return RESOLVER.mapMethodName( + "intermediary", + ownerType.intermediary().getClassName(), + intermediary(), + intermediaryDesc().getDescriptor() + ); + } + } } diff --git a/src/main/java/moe/nea/firmament/init/MixinPlugin.java b/src/main/java/moe/nea/firmament/init/MixinPlugin.java index 61e8f14..d48139b 100644 --- a/src/main/java/moe/nea/firmament/init/MixinPlugin.java +++ b/src/main/java/moe/nea/firmament/init/MixinPlugin.java @@ -8,54 +8,69 @@ import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class MixinPlugin implements IMixinConfigPlugin { - AutoDiscoveryPlugin autoDiscoveryPlugin = new AutoDiscoveryPlugin(); - public static String mixinPackage; - @Override - public void onLoad(String mixinPackage) { - MixinExtrasBootstrap.init(); - MixinPlugin.mixinPackage = mixinPackage; - autoDiscoveryPlugin.setMixinPackage(mixinPackage); - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - if (!Boolean.getBoolean("firmament.debug") && mixinClassName.contains("devenv.")) { - return false; - } - return true; - } - - @Override - public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { - - } - - @Override - public List<String> getMixins() { - return autoDiscoveryPlugin.getMixins().stream().filter(it -> this.shouldApplyMixin(null, it)) - .toList(); - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - public static List<String> appliedMixins = new ArrayList<>(); - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - appliedMixins.add(mixinClassName); - } + AutoDiscoveryPlugin autoDiscoveryPlugin = new AutoDiscoveryPlugin(); + public static List<MixinPlugin> instances = new ArrayList<>(); + public String mixinPackage; + + @Override + public void onLoad(String mixinPackage) { + MixinExtrasBootstrap.init(); + instances.add(this); + this.mixinPackage = mixinPackage; + autoDiscoveryPlugin.setMixinPackage(mixinPackage); + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (!Boolean.getBoolean("firmament.debug") && mixinClassName.contains("devenv.")) { + return false; + } + return true; + } + + @Override + public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { + + } + + @Override + public List<String> getMixins() { + return autoDiscoveryPlugin.getMixins().stream().filter(it -> this.shouldApplyMixin(null, it)) + .toList(); + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + public Set<String> getAppliedFullPathMixins() { + return new HashSet<>(appliedMixins); + } + + public Set<String> getExpectedFullPathMixins() { + return getMixins() + .stream() + .map(it -> mixinPackage + "." + it) + .collect(Collectors.toSet()); + } + + public List<String> appliedMixins = new ArrayList<>(); + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + appliedMixins.add(mixinClassName); + } } diff --git a/src/main/java/moe/nea/firmament/init/RiserUtils.java b/src/main/java/moe/nea/firmament/init/RiserUtils.java index c1c8fd1..ad4ac8f 100644 --- a/src/main/java/moe/nea/firmament/init/RiserUtils.java +++ b/src/main/java/moe/nea/firmament/init/RiserUtils.java @@ -1,12 +1,15 @@ package moe.nea.firmament.init; +import me.shedaniel.mm.api.ClassTinkerers; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.MappingResolver; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; +import java.util.function.Consumer; + public abstract class RiserUtils { protected Type getTypeForClassName(String className) { return Type.getObjectType(className.replace('.', '/')); @@ -24,4 +27,8 @@ public abstract class RiserUtils { return null; } + public void addTransformation(Intermediary.InterClass interClass, Consumer<ClassNode> transformer, boolean post) { + ClassTinkerers.addTransformation(interClass.mapped().getClassName(), transformer, post); + } + } diff --git a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java index f2c6c53..a5d5c1d 100644 --- a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java +++ b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java @@ -1,13 +1,11 @@ package moe.nea.firmament.init; -import me.shedaniel.mm.api.ClassTinkerers; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.block.BlockState; -import net.minecraft.client.render.block.BlockModels; -import net.minecraft.client.render.block.BlockRenderManager; -import net.minecraft.client.render.chunk.SectionBuilder; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.util.math.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.chunk.SectionCompiler; +import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.core.BlockPos; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; @@ -20,98 +18,89 @@ import org.objectweb.asm.tree.VarInsnNode; public class SectionBuilderRiser extends RiserUtils { - @IntermediaryName(SectionBuilder.class) - String SectionBuilder; - @IntermediaryName(BlockPos.class) - String BlockPos; - @IntermediaryName(BlockRenderManager.class) - String BlockRenderManager; - @IntermediaryName(BlockState.class) - String BlockState; - @IntermediaryName(BakedModel.class) - String BakedModel; - String CustomBlockTextures = "moe.nea.firmament.features.texturepack.CustomBlockTextures"; + Intermediary.InterClass SectionBuilder = Intermediary.<SectionCompiler>intermediaryClass(); + Intermediary.InterClass BlockPos = Intermediary.<BlockPos>intermediaryClass(); + Intermediary.InterClass BlockRenderManager = Intermediary.<BlockRenderDispatcher>intermediaryClass(); + Intermediary.InterClass BlockState = Intermediary.<BlockState>intermediaryClass(); + Intermediary.InterClass BlockStateModel = Intermediary.<BlockStateModel>intermediaryClass(); + String CustomBlockTextures = "moe.nea.firmament.features.texturepack.CustomBlockTextures"; - Type getModelDesc = Type.getMethodType( - getTypeForClassName(BlockRenderManager), - getTypeForClassName(BlockState) - ); - String getModel = remapper.mapMethodName( - "intermediary", - Intermediary.<BlockRenderManager>className(), - Intermediary.methodName(net.minecraft.client.render.block.BlockRenderManager::getModel), - Type.getMethodDescriptor( - getTypeForClassName(Intermediary.<BakedModel>className()), - getTypeForClassName(Intermediary.<BlockState>className()) - ) - ); + Intermediary.InterMethod getModel = + Intermediary.intermediaryMethod( + net.minecraft.client.renderer.block.BlockRenderDispatcher::getBlockModel, + BlockStateModel, + BlockState + ); - @Override - public void addTinkerers() { - if (FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo")) - ClassTinkerers.addTransformation(SectionBuilder, this::handle, true); - } + @Override + public void addTinkerers() { + if (FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo")) + addTransformation(SectionBuilder, this::handle, true); + } - private void handle(ClassNode classNode) { - for (MethodNode method : classNode.methods) { - if ((method.name.endsWith("$fabric-renderer-indigo$hookBuildRenderBlock") - || method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate")) && - method.name.startsWith("redirect$")) { - handleIndigo(method); - return; - } - } - System.err.println("Could not inject indigo rendering hook. Is a custom renderer installed (e.g. sodium)?"); - } + private void handle(ClassNode classNode) { + System.out.println("AVAST! "+ getModel); + for (MethodNode method : classNode.methods) { + if ((method.name.endsWith("$fabric-renderer-indigo$hookBuildRenderBlock") + || method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate")) && + method.name.startsWith("redirect$")) { + handleIndigo(method); + return; + } + } + System.err.println("Could not inject indigo rendering hook. Is a custom renderer installed (e.g. sodium)?"); + } - private void handleIndigo(MethodNode method) { - LocalVariableNode blockPosVar = null, blockStateVar = null; - for (LocalVariableNode localVariable : method.localVariables) { - if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockPos))) { - blockPosVar = localVariable; - } - if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockState))) { - blockStateVar = localVariable; - } - } - if (blockPosVar == null || blockStateVar == null) { - System.err.println("Firmament could inject into indigo: missing either block pos or blockstate"); - return; - } - for (AbstractInsnNode instruction : method.instructions) { - if (instruction.getOpcode() != Opcodes.INVOKEVIRTUAL) continue; - var methodInsn = (MethodInsnNode) instruction; - if (!(methodInsn.name.equals(getModel) && Type.getObjectType(methodInsn.owner).equals(getTypeForClassName(BlockRenderManager)))) - continue; - method.instructions.insertBefore( - methodInsn, - new MethodInsnNode( - Opcodes.INVOKESTATIC, - getTypeForClassName(CustomBlockTextures).getInternalName(), - "enterFallbackCall", - Type.getMethodDescriptor(Type.VOID_TYPE) - )); + private void handleIndigo(MethodNode method) { + LocalVariableNode blockPosVar = null, blockStateVar = null; + for (LocalVariableNode localVariable : method.localVariables) { + if (Type.getType(localVariable.desc).equals(BlockPos.mapped())) { + blockPosVar = localVariable; + } + if (Type.getType(localVariable.desc).equals(BlockState.mapped())) { + blockStateVar = localVariable; + } + } + if (blockPosVar == null || blockStateVar == null) { + System.err.println("Firmament could inject into indigo: missing either block pos or blockstate"); + return; + } + for (AbstractInsnNode instruction : method.instructions) { + if (instruction.getOpcode() != Opcodes.INVOKEVIRTUAL) continue; + var methodInsn = (MethodInsnNode) instruction; + if (!(methodInsn.name.equals(getModel.mapped()) && + Type.getObjectType(methodInsn.owner).equals(BlockRenderManager.mapped()))) + continue; + method.instructions.insertBefore( + methodInsn, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + getTypeForClassName(CustomBlockTextures).getInternalName(), + "enterFallbackCall", + Type.getMethodDescriptor(Type.VOID_TYPE) + )); - var insnList = new InsnList(); - insnList.add(new MethodInsnNode( - Opcodes.INVOKESTATIC, - getTypeForClassName(CustomBlockTextures).getInternalName(), - "exitFallbackCall", - Type.getMethodDescriptor(Type.VOID_TYPE) - )); - insnList.add(new VarInsnNode(Opcodes.ALOAD, blockPosVar.index)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, blockStateVar.index)); - insnList.add(new MethodInsnNode( - Opcodes.INVOKESTATIC, - getTypeForClassName(CustomBlockTextures).getInternalName(), - "patchIndigo", - Type.getMethodDescriptor(getTypeForClassName(BakedModel), - getTypeForClassName(BakedModel), - getTypeForClassName(BlockPos), - getTypeForClassName(BlockState)), - false - )); - method.instructions.insert(methodInsn, insnList); - } - } + var insnList = new InsnList(); + insnList.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + getTypeForClassName(CustomBlockTextures).getInternalName(), + "exitFallbackCall", + Type.getMethodDescriptor(Type.VOID_TYPE) + )); + insnList.add(new VarInsnNode(Opcodes.ALOAD, blockPosVar.index)); + insnList.add(new VarInsnNode(Opcodes.ALOAD, blockStateVar.index)); + insnList.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + getTypeForClassName(CustomBlockTextures).getInternalName(), + "patchIndigo", + Type.getMethodDescriptor( + (BlockStateModel).mapped(), + (BlockStateModel).mapped(), + (BlockPos).mapped(), + (BlockState).mapped()), + false + )); + method.instructions.insert(methodInsn, insnList); + } + } } diff --git a/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java b/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java index 22ce991..30b5020 100644 --- a/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java +++ b/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java @@ -1,28 +1,34 @@ package moe.nea.firmament.mixins; +import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.repo.RepoModResourcePack; import net.fabricmc.fabric.api.resource.ModResourcePack; +import net.fabricmc.fabric.impl.resource.loader.ModResourcePackSorter; import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil; -import net.minecraft.resource.ResourceType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.packs.PackType; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.List; @Mixin(ModResourcePackUtil.class) public class AppendRepoAsResourcePack { - @Inject(method = "appendModResourcePacks", at = @At("TAIL")) - private static void onAppendModResourcePack( - List<ModResourcePack> packs, - ResourceType type, - @Nullable String subPath, - CallbackInfo ci - ) { - RepoModResourcePack.Companion.append(packs); - } + @Inject( + method = "getModResourcePacks", + at = @At(value = "INVOKE", target = "Lnet/fabricmc/fabric/impl/resource/loader/ModResourcePackSorter;getPacks()Ljava/util/List;"), + require = 0 + ) + private static void onAppendModResourcePack( + FabricLoader fabricLoader, PackType type, @Nullable String subPath, CallbackInfoReturnable<List<ModResourcePack>> cir, + @Local ModResourcePackSorter sorter + ) { + RepoModResourcePack.Companion.append(sorter); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/BandAidResourcePackPatch.java b/src/main/java/moe/nea/firmament/mixins/BandAidResourcePackPatch.java index d898c44..8e591bd 100644 --- a/src/main/java/moe/nea/firmament/mixins/BandAidResourcePackPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/BandAidResourcePackPatch.java @@ -4,22 +4,22 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.repo.RepoModResourcePack; -import net.minecraft.resource.ReloadableResourceManagerImpl; -import net.minecraft.resource.Resource; -import net.minecraft.util.Identifier; +import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import java.util.Optional; -@Mixin(ReloadableResourceManagerImpl.class) +@Mixin(ReloadableResourceManager.class) public class BandAidResourcePackPatch { @ModifyReturnValue( method = "getResource", at = @At("RETURN") ) - private Optional<Resource> injectOurCustomResourcesInCaseExistingMethodsFailed(Optional<Resource> original, @Local Identifier identifier) { + private Optional<Resource> injectOurCustomResourcesInCaseExistingMethodsFailed(Optional<Resource> original, @Local ResourceLocation identifier) { return original.or(() -> RepoModResourcePack.Companion.createResourceDirectly(identifier)); } } diff --git a/src/main/java/moe/nea/firmament/mixins/ChatPeekScrollPatch.java b/src/main/java/moe/nea/firmament/mixins/ChatPeekScrollPatch.java new file mode 100644 index 0000000..c795908 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/ChatPeekScrollPatch.java @@ -0,0 +1,27 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.features.fixes.Fixes; +import net.minecraft.client.Minecraft; +import net.minecraft.client.MouseHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MouseHandler.class) +public class ChatPeekScrollPatch { + + @Inject(method = "onScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Inventory;getSelectedSlot()I"), cancellable = true) + public void onHotbarScrollWhilePeeking(long window, double horizontal, double vertical, CallbackInfo ci) { + if (Fixes.INSTANCE.shouldPeekChat() && Fixes.INSTANCE.shouldScrollPeekedChat()) ci.cancel(); + } + + @ModifyVariable(method = "onScroll", at = @At(value = "STORE"), ordinal = 0) + public int onGetChatHud(int i) { + if (Fixes.INSTANCE.shouldPeekChat() && Fixes.INSTANCE.shouldScrollPeekedChat()) + Minecraft.getInstance().gui.getChat().scrollChat(i); + return i; + } + +} diff --git a/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java b/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java index 9f6fb4d..2bc1374 100644 --- a/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/ChatPeekingPatch.java @@ -4,12 +4,12 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import moe.nea.firmament.features.fixes.Fixes; -import net.minecraft.client.gui.hud.ChatHud; +import net.minecraft.client.gui.components.ChatComponent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; -@Mixin(ChatHud.class) +@Mixin(ChatComponent.class) public class ChatPeekingPatch { @ModifyVariable(method = "render", at = @At(value = "HEAD"), index = 5, argsOnly = true) @@ -17,7 +17,7 @@ public class ChatPeekingPatch { return old || Fixes.INSTANCE.shouldPeekChat(); } - @ModifyExpressionValue(method = "getHeight()I", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z")) + @ModifyExpressionValue(method = "getHeight()I", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;isChatFocused()Z")) public boolean onGetChatHudHeight(boolean old) { return old || Fixes.INSTANCE.shouldPeekChat(); } diff --git a/src/main/java/moe/nea/firmament/mixins/CopyChatPatch.java b/src/main/java/moe/nea/firmament/mixins/CopyChatPatch.java new file mode 100644 index 0000000..9079fc9 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/CopyChatPatch.java @@ -0,0 +1,45 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.features.chat.CopyChat; +import moe.nea.firmament.mixins.accessor.AccessorChatHud; +import moe.nea.firmament.util.ClipboardUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.gui.components.ChatComponent; +import net.minecraft.client.GuiMessage; +import net.minecraft.client.gui.screens.ChatScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.ChatFormatting; +import net.minecraft.util.Mth; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.List; + +@Mixin(ChatScreen.class) +public class CopyChatPatch { + @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) + private void onRightClick(MouseButtonEvent click, boolean doubled, CallbackInfoReturnable<Boolean> cir) throws NoSuchFieldException, IllegalAccessException { + if (click.button() != 1 || !CopyChat.TConfig.INSTANCE.getCopyChat()) return; + Minecraft client = Minecraft.getInstance(); + ChatComponent chatHud = client.gui.getChat(); + int lineIndex = getChatLineIndex(chatHud, click.y()); + if (lineIndex < 0) return; + List<GuiMessage.Line> visible = ((AccessorChatHud) chatHud).getVisibleMessages_firmament(); + if (lineIndex >= visible.size()) return; + GuiMessage.Line line = visible.get(lineIndex); + String text = CopyChat.INSTANCE.orderedTextToString(line.content()); + ClipboardUtils.INSTANCE.setTextContent(text); + chatHud.addMessage(Component.literal("Copied: ").append(text).withStyle(ChatFormatting.GRAY)); + cir.setReturnValue(true); + cir.cancel(); + } + + @Unique + private int getChatLineIndex(ChatComponent chatHud, double mouseY) { + double chatLineY = ((AccessorChatHud) chatHud).toChatLineY_firmament(mouseY); + return Mth.floor(chatLineY + ((AccessorChatHud) chatHud).getScrolledLines_firmament()); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java index fde3580..2299068 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java @@ -6,20 +6,20 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import moe.nea.firmament.util.DurabilityBarEvent; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.item.ItemStack; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -@Mixin(DrawContext.class) +@Mixin(GuiGraphics.class) public class CustomDurabilityBarPatch { @WrapOperation( - method = "drawItemBar", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z") + method = "renderItemBar", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isBarVisible()Z") ) private boolean onIsItemBarVisible( - ItemStack instance, Operation<Boolean> original, - @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride + ItemStack instance, Operation<Boolean> original, + @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride ) { if (original.call(instance)) return true; @@ -29,22 +29,22 @@ public class CustomDurabilityBarPatch { return barOverride.get() != null; } - @WrapOperation(method = "drawItemBar", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarStep()I")) + @WrapOperation(method = "renderItemBar", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;getBarWidth()I")) private int overrideItemStep( - ItemStack instance, Operation<Integer> original, - @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride + ItemStack instance, Operation<Integer> original, + @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride ) { if (barOverride.get() != null) return Math.round(barOverride.get().getPercentage() * 13); return original.call(instance); } - @WrapOperation(method = "drawItemBar", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarColor()I")) + @WrapOperation(method = "renderItemBar", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;getBarColor()I")) private int overrideItemColor( - ItemStack instance, Operation<Integer> original, - @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride + ItemStack instance, Operation<Integer> original, + @Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride ) { if (barOverride.get() != null) return barOverride.get().getColor().getColor(); diff --git a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java index 717d404..8503411 100644 --- a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java @@ -6,8 +6,8 @@ import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.datafixers.util.Pair; -import net.minecraft.datafixer.TypeReferences; -import net.minecraft.datafixer.fix.EntityIdFix; +import net.minecraft.util.datafix.fixes.References; +import net.minecraft.util.datafix.fixes.EntityIdFix; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -22,7 +22,7 @@ import java.util.Map; public abstract class DFUEntityIdFixPatch extends DataFix { @Shadow @Final - private static Map<String, String> RENAMED_ENTITIES; + private static Map<String, String> ID_MAP; public DFUEntityIdFixPatch(Schema outputSchema, boolean changesType) { super(outputSchema, changesType); @@ -30,6 +30,6 @@ public abstract class DFUEntityIdFixPatch extends DataFix { @Inject(method = "makeRule", at = @At("RETURN"), cancellable = true) public void onMakeRule(CallbackInfoReturnable<TypeRewriteRule> cir) { - cir.setReturnValue(TypeRewriteRule.seq(fixTypeEverywhere("EntityIdFix", getInputSchema().findChoiceType(TypeReferences.ENTITY), getOutputSchema().findChoiceType(TypeReferences.ENTITY), dynamicOps -> pair -> ((Pair) pair).mapFirst(string -> RENAMED_ENTITIES.getOrDefault(string, (String) string))), convertUnchecked("Fix Type", getInputSchema().getType(TypeReferences.ITEM_STACK), getOutputSchema().getType(TypeReferences.ITEM_STACK)))); + cir.setReturnValue(TypeRewriteRule.seq(fixTypeEverywhere("EntityIdFix", getInputSchema().findChoiceType(References.ENTITY), getOutputSchema().findChoiceType(References.ENTITY), dynamicOps -> pair -> ((Pair) pair).mapFirst(string -> ID_MAP.getOrDefault(string, (String) string))), convertUnchecked("Fix Type", getInputSchema().getType(References.ITEM_STACK), getOutputSchema().getType(References.ITEM_STACK)))); } } diff --git a/src/main/java/moe/nea/firmament/mixins/DisableHurtCam.java b/src/main/java/moe/nea/firmament/mixins/DisableHurtCam.java new file mode 100644 index 0000000..3a53ab1 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/DisableHurtCam.java @@ -0,0 +1,18 @@ +package moe.nea.firmament.mixins; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.features.fixes.Fixes; +import net.minecraft.client.renderer.GameRenderer; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GameRenderer.class) +public class DisableHurtCam { + @ModifyExpressionValue(method = "bobHurt", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;hurtTime:I", opcode = Opcodes.GETFIELD)) + private int replaceHurtTime(int original) { + if (Fixes.TConfig.INSTANCE.getNoHurtCam()) + return 0; + return original; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/DispatchMouseInputEventsPatch.java b/src/main/java/moe/nea/firmament/mixins/DispatchMouseInputEventsPatch.java new file mode 100644 index 0000000..ecd361d --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/DispatchMouseInputEventsPatch.java @@ -0,0 +1,17 @@ +package moe.nea.firmament.mixins; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import moe.nea.firmament.events.WorldMouseMoveEvent; +import net.minecraft.client.MouseHandler; +import net.minecraft.client.player.LocalPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(MouseHandler.class) +public class DispatchMouseInputEventsPatch { + @WrapWithCondition(method = "turnPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V")) + public boolean onRotatePlayer(LocalPlayer instance, double deltaX, double deltaY) { + var event = WorldMouseMoveEvent.Companion.publish(new WorldMouseMoveEvent(deltaX, deltaY)); + return !event.getCancelled(); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/EarlyResourceReloadPatch.java b/src/main/java/moe/nea/firmament/mixins/EarlyResourceReloadPatch.java index e98faf6..b8460a7 100644 --- a/src/main/java/moe/nea/firmament/mixins/EarlyResourceReloadPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/EarlyResourceReloadPatch.java @@ -2,10 +2,10 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.EarlyResourceReloadEvent; -import net.minecraft.resource.ReloadableResourceManagerImpl; -import net.minecraft.resource.ResourceManager; -import net.minecraft.resource.ResourcePack; -import net.minecraft.resource.ResourceReload; +import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.PackResources; +import net.minecraft.server.packs.resources.ReloadInstance; import net.minecraft.util.Unit; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -16,10 +16,10 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -@Mixin(ReloadableResourceManagerImpl.class) +@Mixin(ReloadableResourceManager.class) public abstract class EarlyResourceReloadPatch implements ResourceManager { - @Inject(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/SimpleResourceReload;start(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Z)Lnet/minecraft/resource/ResourceReload;", shift = At.Shift.BEFORE)) - public void onResourceReload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture<Unit> initialStage, List<ResourcePack> packs, CallbackInfoReturnable<ResourceReload> cir) { + @Inject(method = "createReload", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/SimpleReloadInstance;create(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Z)Lnet/minecraft/server/packs/resources/ReloadInstance;", shift = At.Shift.BEFORE)) + public void onResourceReload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture<Unit> initialStage, List<PackResources> packs, CallbackInfoReturnable<ReloadInstance> cir) { EarlyResourceReloadEvent.Companion.publish(new EarlyResourceReloadEvent(this, prepareExecutor)); } } diff --git a/src/main/java/moe/nea/firmament/mixins/EntityDespawnPatch.java b/src/main/java/moe/nea/firmament/mixins/EntityDespawnPatch.java index 22bebec..c286226 100644 --- a/src/main/java/moe/nea/firmament/mixins/EntityDespawnPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/EntityDespawnPatch.java @@ -3,15 +3,15 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.EntityDespawnEvent; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientWorld.class) +@Mixin(ClientLevel.class) public class EntityDespawnPatch { @Inject(method = "removeEntity", at = @At(value = "TAIL")) private void onRemoved(int entityId, Entity.RemovalReason removalReason, CallbackInfo ci, @Local @Nullable Entity entity) { diff --git a/src/main/java/moe/nea/firmament/mixins/EntityInteractEventPatch.java b/src/main/java/moe/nea/firmament/mixins/EntityInteractEventPatch.java index 8ade59b..4138389 100644 --- a/src/main/java/moe/nea/firmament/mixins/EntityInteractEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/EntityInteractEventPatch.java @@ -2,32 +2,32 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.EntityInteractionEvent; -import net.minecraft.client.network.ClientPlayerInteractionManager; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.EntityHitResult; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(ClientPlayerInteractionManager.class) +@Mixin(MultiPlayerGameMode.class) public class EntityInteractEventPatch { - @Inject(method = "attackEntity", at = @At("HEAD")) - private void onAttack(PlayerEntity player, Entity target, CallbackInfo ci) { - EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.ATTACK, target, Hand.MAIN_HAND)); + @Inject(method = "attack", at = @At("HEAD")) + private void onAttack(Player player, Entity target, CallbackInfo ci) { + EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.ATTACK, target, InteractionHand.MAIN_HAND)); } - @Inject(method = "interactEntity", at = @At("HEAD")) - private void onInteract(PlayerEntity player, Entity entity, Hand hand, CallbackInfoReturnable<ActionResult> cir) { + @Inject(method = "interact", at = @At("HEAD")) + private void onInteract(Player player, Entity entity, InteractionHand hand, CallbackInfoReturnable<InteractionResult> cir) { EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.INTERACT, entity, hand)); } - @Inject(method = "interactEntityAtLocation", at = @At("HEAD")) - private void onInteractAtLocation(PlayerEntity player, Entity entity, EntityHitResult hitResult, Hand hand, CallbackInfoReturnable<ActionResult> cir) { + @Inject(method = "interactAt", at = @At("HEAD")) + private void onInteractAtLocation(Player player, Entity entity, EntityHitResult hitResult, InteractionHand hand, CallbackInfoReturnable<InteractionResult> cir) { EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.INTERACT_AT_LOCATION, entity, hand)); } diff --git a/src/main/java/moe/nea/firmament/mixins/EntityUpdateEventListener.java b/src/main/java/moe/nea/firmament/mixins/EntityUpdateEventListener.java index c2d6e46..0021099 100644 --- a/src/main/java/moe/nea/firmament/mixins/EntityUpdateEventListener.java +++ b/src/main/java/moe/nea/firmament/mixins/EntityUpdateEventListener.java @@ -3,40 +3,46 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.EntityUpdateEvent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientCommonNetworkHandler; -import net.minecraft.client.network.ClientConnectionState; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.packet.s2c.play.EntityAttributesS2CPacket; -import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; +import net.minecraft.client.multiplayer.CommonListenerCookie; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; +import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayNetworkHandler.class) -public abstract class EntityUpdateEventListener extends ClientCommonNetworkHandler { +@Mixin(ClientPacketListener.class) +public abstract class EntityUpdateEventListener extends ClientCommonPacketListenerImpl { - @Shadow - private ClientWorld world; + @Shadow + private ClientLevel level; - protected EntityUpdateEventListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) { - super(client, connection, connectionState); - } + protected EntityUpdateEventListener(Minecraft client, Connection connection, CommonListenerCookie connectionState) { + super(client, connection, connectionState); + } - @Inject(method = "onEntityAttributes", at = @At("TAIL")) - private void onAttributeUpdate(EntityAttributesS2CPacket packet, CallbackInfo ci) { - EntityUpdateEvent.Companion.publish(new EntityUpdateEvent.AttributeUpdate( - (LivingEntity) world.getEntityById(packet.getEntityId()), packet.getEntries())); - } + @Inject(method = "handleSetEquipment", at = @At(value = "INVOKE", target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V", shift = At.Shift.AFTER)) + private void onEquipmentUpdate(ClientboundSetEquipmentPacket packet, CallbackInfo ci, @Local LivingEntity entity) { + EntityUpdateEvent.Companion.publish(new EntityUpdateEvent.EquipmentUpdate(entity, packet.getSlots())); + } - @Inject(method = "onEntityTrackerUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/data/DataTracker;writeUpdatedEntries(Ljava/util/List;)V", shift = At.Shift.AFTER)) - private void onEntityTracker(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { - EntityUpdateEvent.Companion.publish(new EntityUpdateEvent.TrackedDataUpdate(entity, packet.trackedValues())); - } + @Inject(method = "handleUpdateAttributes", at = @At("TAIL")) + private void onAttributeUpdate(ClientboundUpdateAttributesPacket packet, CallbackInfo ci) { + EntityUpdateEvent.Companion.publish(new EntityUpdateEvent.AttributeUpdate( + (LivingEntity) level.getEntity(packet.getEntityId()), packet.getValues())); + } + + @Inject(method = "handleSetEntityData", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/syncher/SynchedEntityData;assignValues(Ljava/util/List;)V", shift = At.Shift.AFTER)) + private void onEntityTracker(ClientboundSetEntityDataPacket packet, CallbackInfo ci, @Local Entity entity) { + EntityUpdateEvent.Companion.publish(new EntityUpdateEvent.TrackedDataUpdate(entity, packet.packedItems())); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java b/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java index 699d5b7..5d8484f 100644 --- a/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/FirmKeybindsInVanillaControlsPatch.java @@ -3,13 +3,12 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.gui.config.KeyBindingHandler; -import moe.nea.firmament.gui.config.ManagedConfig; import moe.nea.firmament.keybindings.FirmamentKeyBindings; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.option.ControlsListWidget; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.option.KeyBinding; -import net.minecraft.text.Text; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.options.controls.KeyBindsList; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.KeyMapping; +import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -19,39 +18,39 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ControlsListWidget.KeyBindingEntry.class) +@Mixin(KeyBindsList.KeyEntry.class) public class FirmKeybindsInVanillaControlsPatch { @Mutable @Shadow @Final - private ButtonWidget editButton; + private Button changeButton; @Shadow @Final - private KeyBinding binding; + private KeyMapping key; @Shadow @Final - private ButtonWidget resetButton; + private Button resetButton; - @ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ButtonWidget;builder(Lnet/minecraft/text/Text;Lnet/minecraft/client/gui/widget/ButtonWidget$PressAction;)Lnet/minecraft/client/gui/widget/ButtonWidget$Builder;")) - public ButtonWidget.PressAction onInit(ButtonWidget.PressAction action) { - var config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(binding); + @ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/Button;builder(Lnet/minecraft/network/chat/Component;Lnet/minecraft/client/gui/components/Button$OnPress;)Lnet/minecraft/client/gui/components/Button$Builder;")) + public Button.OnPress onInit(Button.OnPress action) { + var config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(key); if (config == null) return action; return button -> { ((KeyBindingHandler) config.getHandler()) .getManagedConfig() - .showConfigEditor(MinecraftClient.getInstance().currentScreen); + .showConfigEditor(Minecraft.getInstance().screen); }; } - @Inject(method = "update", at = @At("HEAD"), cancellable = true) + @Inject(method = "refreshEntry", at = @At("HEAD"), cancellable = true) public void onUpdate(CallbackInfo ci) { - var config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(binding); + var config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(key); if (config == null) return; resetButton.active = false; - editButton.setMessage(Text.translatable("firmament.keybinding.external", config.getValue().format())); + changeButton.setMessage(Component.translatable("firmament.keybinding.external", config.getValue().format())); ci.cancel(); } diff --git a/src/main/java/moe/nea/firmament/mixins/HideStatusEffectsPatch.java b/src/main/java/moe/nea/firmament/mixins/HideStatusEffectsPatch.java index c5af8b6..b2884cd 100644 --- a/src/main/java/moe/nea/firmament/mixins/HideStatusEffectsPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/HideStatusEffectsPatch.java @@ -1,29 +1,33 @@ package moe.nea.firmament.mixins; -import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import moe.nea.firmament.features.fixes.Fixes; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.ingame.StatusEffectsDisplay; +import moe.nea.firmament.util.SBData; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.EffectsInInventory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(InventoryScreen.class) +@Mixin(EffectsInInventory.class) public abstract class HideStatusEffectsPatch { @Shadow - public abstract boolean shouldHideStatusEffectHud(); + public abstract boolean canSeeEffects(); - @Inject(method = "shouldHideStatusEffectHud", at = @At("HEAD"), cancellable = true) + @Inject(method = "canSeeEffects", at = @At("HEAD"), cancellable = true) private void hideStatusEffects(CallbackInfoReturnable<Boolean> cir) { - cir.setReturnValue(!Fixes.TConfig.INSTANCE.getHidePotionEffects()); + if (Fixes.TConfig.INSTANCE.getHidePotionEffects()) { + cir.setReturnValue(false); + } } - @WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/StatusEffectsDisplay;drawStatusEffects(Lnet/minecraft/client/gui/DrawContext;IIF)V")) - private boolean conditionalRenderStatuses(StatusEffectsDisplay instance, DrawContext context, int mouseX, int mouseY, float tickDelta) { - return shouldHideStatusEffectHud() || !Fixes.TConfig.INSTANCE.getHidePotionEffects(); + @Inject(method = "renderEffects", at = @At("HEAD"), cancellable = true) + private void conditionalRenderStatuses(GuiGraphics context, int mouseX, int mouseY, CallbackInfo ci) { + if (Fixes.TConfig.INSTANCE.getHidePotionEffects()) { + ci.cancel(); + } } } diff --git a/src/main/java/moe/nea/firmament/mixins/HudRenderEventsPatch.java b/src/main/java/moe/nea/firmament/mixins/HudRenderEventsPatch.java index 85c0462..1f7e07a 100644 --- a/src/main/java/moe/nea/firmament/mixins/HudRenderEventsPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/HudRenderEventsPatch.java @@ -4,26 +4,34 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.HotbarItemRenderEvent; import moe.nea.firmament.events.HudRenderEvent; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.hud.InGameHud; -import net.minecraft.client.render.RenderTickCounter; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import moe.nea.firmament.features.fixes.Fixes; +import moe.nea.firmament.util.SBData; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.DeltaTracker; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(InGameHud.class) +@Mixin(Gui.class) public class HudRenderEventsPatch { @Inject(method = "renderSleepOverlay", at = @At(value = "HEAD")) - public void renderCallBack(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) { + public void renderCallBack(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci) { HudRenderEvent.Companion.publish(new HudRenderEvent(context, tickCounter)); } - @Inject(method = "renderHotbarItem", at = @At("HEAD")) - public void onRenderHotbarItem(DrawContext context, int x, int y, RenderTickCounter tickCounter, PlayerEntity player, ItemStack stack, int seed, CallbackInfo ci) { + @Inject(method = "renderSlot", at = @At("HEAD")) + public void onRenderHotbarItem(GuiGraphics context, int x, int y, DeltaTracker tickCounter, Player player, ItemStack stack, int seed, CallbackInfo ci) { if (stack != null && !stack.isEmpty()) HotbarItemRenderEvent.Companion.publish(new HotbarItemRenderEvent(stack, context, x, y, tickCounter)); } + + @Inject(method = "renderEffects", at = @At("HEAD"), cancellable = true) + public void hideStatusEffects(CallbackInfo ci) { + if (Fixes.TConfig.INSTANCE.getHidePotionEffectsHud() && SBData.INSTANCE.isOnSkyblock()) ci.cancel(); + } + } diff --git a/src/main/java/moe/nea/firmament/mixins/IncomingPacketListenerPatches.java b/src/main/java/moe/nea/firmament/mixins/IncomingPacketListenerPatches.java index a7c3875..9f338f5 100644 --- a/src/main/java/moe/nea/firmament/mixins/IncomingPacketListenerPatches.java +++ b/src/main/java/moe/nea/firmament/mixins/IncomingPacketListenerPatches.java @@ -6,33 +6,33 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.mojang.brigadier.CommandDispatcher; import moe.nea.firmament.events.MaskCommands; import moe.nea.firmament.events.ParticleSpawnEvent; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; -import net.minecraft.util.math.Vec3d; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; +import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayNetworkHandler.class) +@Mixin(ClientPacketListener.class) public abstract class IncomingPacketListenerPatches { - @ModifyExpressionValue(method = "onCommandTree", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false)) + @ModifyExpressionValue(method = "handleCommands", at = @At(value = "NEW", target = "(Lcom/mojang/brigadier/tree/RootCommandNode;)Lcom/mojang/brigadier/CommandDispatcher;", remap = false)) public CommandDispatcher onOnCommandTree(CommandDispatcher dispatcher) { MaskCommands.Companion.publish(new MaskCommands(dispatcher)); return dispatcher; } - @Inject(method = "onParticle", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", shift = At.Shift.AFTER), cancellable = true) - public void onParticleSpawn(ParticleS2CPacket packet, CallbackInfo ci) { + @Inject(method = "handleParticleEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/network/PacketProcessor;)V", shift = At.Shift.AFTER), cancellable = true) + public void onParticleSpawn(ClientboundLevelParticlesPacket packet, CallbackInfo ci) { var event = new ParticleSpawnEvent( - packet.getParameters(), - new Vec3d(packet.getX(), packet.getY(), packet.getZ()), - new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()), - packet.isImportant(), + packet.getParticle(), + new Vec3(packet.getX(), packet.getY(), packet.getZ()), + new Vector3f(packet.getXDist(), packet.getYDist(), packet.getZDist()), + packet.alwaysShow(), packet.getCount(), - packet.getSpeed() + packet.getMaxSpeed() ); ParticleSpawnEvent.Companion.publish(event); if (event.getCancelled()) diff --git a/src/main/java/moe/nea/firmament/mixins/KeyPressInWorldEventPatch.java b/src/main/java/moe/nea/firmament/mixins/KeyPressInWorldEventPatch.java index 48f3c23..ee7f570 100644 --- a/src/main/java/moe/nea/firmament/mixins/KeyPressInWorldEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/KeyPressInWorldEventPatch.java @@ -2,18 +2,23 @@ package moe.nea.firmament.mixins; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.WorldKeyboardEvent; -import net.minecraft.client.Keyboard; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; +import net.minecraft.client.KeyboardHandler; +import net.minecraft.client.input.KeyEvent; +import com.mojang.blaze3d.platform.InputConstants; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(Keyboard.class) +@Mixin(KeyboardHandler.class) public class KeyPressInWorldEventPatch { - @Inject(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/KeyBinding;onKeyPressed(Lnet/minecraft/client/util/InputUtil$Key;)V")) - public void onKeyBoardInWorld(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { - WorldKeyboardEvent.Companion.publish(new WorldKeyboardEvent(key, scancode, modifiers)); - } + @WrapWithCondition(method = "keyPress", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/KeyMapping;click(Lcom/mojang/blaze3d/platform/InputConstants$Key;)V")) + public boolean onKeyBoardInWorld(InputConstants.Key key, @Local(argsOnly = true) KeyEvent keyInput) { + var event = WorldKeyboardEvent.Companion.publish(new WorldKeyboardEvent(GenericInputAction.of(keyInput), InputModifiers.of(keyInput))); + return !event.getCancelled(); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java b/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java deleted file mode 100644 index 76b34ba..0000000 --- a/src/main/java/moe/nea/firmament/mixins/LenientProfileComponentPatch.java +++ /dev/null @@ -1,25 +0,0 @@ - -package moe.nea.firmament.mixins; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.Lifecycle; -import com.mojang.util.UndashedUuid; -import moe.nea.firmament.util.json.FirmCodecs; -import net.minecraft.component.type.ProfileComponent; -import net.minecraft.util.Uuids; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.UUID; - -@Mixin(ProfileComponent.class) -public class LenientProfileComponentPatch { - // lambda in RecordCodecBuilder.create for BASE_CODEC - @ModifyExpressionValue(method = "method_57508", at = @At(value = "FIELD", opcode = Opcodes.GETSTATIC, target = "Lnet/minecraft/util/Uuids;INT_STREAM_CODEC:Lcom/mojang/serialization/Codec;")) - private static Codec<UUID> onStaticInit(Codec<UUID> original) { - return FirmCodecs.UUID_LENIENT_PREFER_INT_STREAM; - } -} diff --git a/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java index 0a90b35..2f82fd9 100644 --- a/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java @@ -2,8 +2,8 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.Firmament; import moe.nea.firmament.events.DebugInstantiateEvent; -import net.minecraft.client.gui.LogoDrawer; -import net.minecraft.client.gui.screen.TitleScreen; +import net.minecraft.client.gui.components.LogoRenderer; +import net.minecraft.client.gui.screens.TitleScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -15,9 +15,9 @@ public class MainWindowFirstLoadPatch { @Unique private static boolean hasInited = false; - @Inject(method = "<init>(ZLnet/minecraft/client/gui/LogoDrawer;)V", at = @At("RETURN")) - private void onCreate(boolean doBackgroundFade, LogoDrawer logoDrawer, CallbackInfo ci) { - if (!hasInited) { + @Inject(method = "<init>(ZLnet/minecraft/client/gui/components/LogoRenderer;)V", at = @At("RETURN")) + private void onCreate(boolean doBackgroundFade, LogoRenderer logoDrawer, CallbackInfo ci) { + if (!hasInited && Firmament.INSTANCE.getDEBUG()) { try { DebugInstantiateEvent.Companion.publish(new DebugInstantiateEvent()); } catch (Throwable t) { diff --git a/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java b/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java new file mode 100644 index 0000000..97d524a --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MaintainKeyboardStatePatch.java @@ -0,0 +1,17 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.keybindings.FirmamentKeyboardState; +import net.minecraft.client.KeyboardHandler; +import net.minecraft.client.input.KeyEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(KeyboardHandler.class) +public class MaintainKeyboardStatePatch { + @Inject(method = "keyPress", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/FramerateLimitTracker;onInputReceived()V")) + private void onKeyInput(long window, int action, KeyEvent input, CallbackInfo ci) { + FirmamentKeyboardState.INSTANCE.maintainState(input, action); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java b/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java new file mode 100644 index 0000000..7c1189b --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java @@ -0,0 +1,26 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.util.mc.InitLevel; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class MinecraftInitLevelListener { + @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;initBackendSystem()Lnet/minecraft/util/TimeSource$NanoTimeSource;")) + private void onInitRenderBackend(CallbackInfo ci) { + InitLevel.bump(InitLevel.RENDER_INIT); + } + + @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;initRenderer(JIZLjava/util/function/BiFunction;Z)V")) + private void onInitRender(CallbackInfo ci) { + InitLevel.bump(InitLevel.RENDER); + } + + @Inject(method = "<init>", at = @At(value = "TAIL")) + private void onFinishedLoading(CallbackInfo ci) { + InitLevel.bump(InitLevel.MAIN_MENU); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index e607ba3..c7555fb 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -4,16 +4,20 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.*; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.slot.Slot; -import net.minecraft.screen.slot.SlotActionType; -import net.minecraft.text.Text; +import moe.nea.firmament.events.HandledScreenClickEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -22,77 +26,68 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import java.util.Iterator; - -@Mixin(value = HandledScreen.class, priority = 990) -public abstract class MixinHandledScreen<T extends ScreenHandler> { +@Mixin(value = AbstractContainerScreen.class, priority = 990) +public abstract class MixinHandledScreen<T extends AbstractContainerMenu> { @Shadow @Final - protected T handler; + protected T menu; @Shadow - public abstract T getScreenHandler(); + public abstract T getMenu(); @Shadow - protected int y; + protected int topPos; @Shadow - protected int x; + protected int leftPos; @Unique - PlayerInventory playerInventory; + Inventory playerInventory; @Inject(method = "<init>", at = @At("TAIL")) - public void savePlayerInventory(ScreenHandler handler, PlayerInventory inventory, Text title, CallbackInfo ci) { + public void savePlayerInventory(AbstractContainerMenu handler, Inventory inventory, Component title, CallbackInfo ci) { this.playerInventory = inventory; } - @Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;handleHotbarKeyPressed(II)Z", shift = At.Shift.BEFORE), cancellable = true) - public void onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) { - if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) { - cir.setReturnValue(true); - } - } - - @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) - public void onMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { - if (HandledScreenClickEvent.Companion.publish(new HandledScreenClickEvent((HandledScreen<?>) (Object) this, mouseX, mouseY, button)).getCancelled()) { + @Inject(method = "mouseReleased", at = @At("HEAD"), cancellable = true) + private void onMouseReleased(MouseButtonEvent click, CallbackInfoReturnable<Boolean> cir) { + var self = (AbstractContainerScreen<?>) (Object) this; + var clickEvent = new HandledScreenClickEvent(self, click.x(), click.y(), click.button()); + var keyEvent = new HandledScreenKeyReleasedEvent(self, GenericInputAction.mouse(click), InputModifiers.current()); + if (HandledScreenClickEvent.Companion.publish(clickEvent).getCancelled() + || HandledScreenKeyReleasedEvent.Companion.publish(keyEvent).getCancelled()) { cir.setReturnValue(true); } } - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawForeground(Lnet/minecraft/client/gui/DrawContext;II)V", shift = At.Shift.AFTER)) - public void onAfterRenderForeground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - context.getMatrices().push(); - context.getMatrices().translate(-x, -y, 0); - HandledScreenForegroundEvent.Companion.publish(new HandledScreenForegroundEvent((HandledScreen<?>) (Object) this, context, mouseX, mouseY, delta)); - context.getMatrices().pop(); + @Inject(method = "renderContents", at = @At("HEAD")) + public void onAfterRenderForeground(GuiGraphics context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + HandledScreenForegroundEvent.Companion.publish(new HandledScreenForegroundEvent((AbstractContainerScreen<?>) (Object) this, context, mouseX, mouseY, delta)); } - @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) - public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { - if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window" - ItemStack cursorStack = getScreenHandler().getCursorStack(); - if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) { + @Inject(method = "slotClicked(Lnet/minecraft/world/inventory/Slot;IILnet/minecraft/world/inventory/ClickType;)V", at = @At("HEAD"), cancellable = true) + public void onMouseClickedSlot(Slot slot, int slotId, int button, ClickType actionType, CallbackInfo ci) { + if (slotId == -999 && getMenu() != null && actionType == ClickType.PICKUP) { // -999 is code for "clicked outside the main window" + ItemStack cursorStack = getMenu().getCarried(); + if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, ClickType.THROW, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE, cursorStack)) { ci.cancel(); return; } } - if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) { + if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) { ci.cancel(); return; } - if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) { - if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) { + if (actionType == ClickType.SWAP && 0 <= button && button < 9) { + if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) { ci.cancel(); } } } - @WrapOperation(method = "drawSlots", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) - public void onDrawSlots(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) { + @WrapOperation(method = "renderSlots", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/AbstractContainerScreen;renderSlot(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/inventory/Slot;)V")) + public void onDrawSlots(AbstractContainerScreen instance, GuiGraphics context, Slot slot, Operation<Void> original) { var before = new SlotRenderEvents.Before(context, slot); SlotRenderEvents.Before.Companion.publish(before); original.call(instance, context, slot); diff --git a/src/main/java/moe/nea/firmament/mixins/MixinPlayerScreenHandler.java b/src/main/java/moe/nea/firmament/mixins/MixinPlayerScreenHandler.java new file mode 100644 index 0000000..2210c9e --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinPlayerScreenHandler.java @@ -0,0 +1,31 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.features.fixes.Fixes; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.InventoryMenu; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(InventoryMenu.class) +public class MixinPlayerScreenHandler { + + @Unique + private static final int OFF_HAND_SLOT = 40; + + @Inject(method = "<init>", at = @At("TAIL")) + private void moveOffHandSlot(Inventory inventory, boolean onServer, Player owner, CallbackInfo ci) { + if (Fixes.TConfig.INSTANCE.getHideOffHand()) { + InventoryMenu self = (InventoryMenu) (Object) this; + self.slots.stream() + .filter(slot -> slot.getContainerSlot() == OFF_HAND_SLOT) + .forEach(slot -> { + slot.x = -1000; + slot.y = -1000; + }); + } + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinRecipeBookScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinRecipeBookScreen.java new file mode 100644 index 0000000..d0ec17c --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinRecipeBookScreen.java @@ -0,0 +1,16 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.features.fixes.Fixes; +import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = AbstractRecipeBookScreen.class, priority = 999) +public class MixinRecipeBookScreen { + @Inject(method = "initButton", at = @At("HEAD"), cancellable = true) + public void addRecipeBook(CallbackInfo ci) { + if (Fixes.TConfig.INSTANCE.getHideRecipeBook()) ci.cancel(); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/MousePressInWorldEventPatch.java b/src/main/java/moe/nea/firmament/mixins/MousePressInWorldEventPatch.java new file mode 100644 index 0000000..e0ae61b --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MousePressInWorldEventPatch.java @@ -0,0 +1,22 @@ +package moe.nea.firmament.mixins; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.events.WorldKeyboardEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; +import net.minecraft.client.MouseHandler; +import net.minecraft.client.input.MouseButtonInfo; +import com.mojang.blaze3d.platform.InputConstants; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(MouseHandler.class) +public class MousePressInWorldEventPatch { + @WrapWithCondition(method = "onButton", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/KeyMapping;click(Lcom/mojang/blaze3d/platform/InputConstants$Key;)V")) + public boolean onKeyBoardInWorld(InputConstants.Key key, @Local(argsOnly = true) MouseButtonInfo input) { // TODO: handle modified mouse click instead + var event = WorldKeyboardEvent.Companion.publish(new WorldKeyboardEvent(GenericInputAction.of(input), + InputModifiers.of(input))); + return !event.getCancelled(); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/OutgoingPacketEventPatch.java b/src/main/java/moe/nea/firmament/mixins/OutgoingPacketEventPatch.java index 25505b7..fd6869c 100644 --- a/src/main/java/moe/nea/firmament/mixins/OutgoingPacketEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/OutgoingPacketEventPatch.java @@ -3,16 +3,16 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.OutgoingPacketEvent; -import net.minecraft.client.network.ClientCommonNetworkHandler; -import net.minecraft.network.packet.Packet; +import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; +import net.minecraft.network.protocol.Packet; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientCommonNetworkHandler.class) +@Mixin(ClientCommonPacketListenerImpl.class) public class OutgoingPacketEventPatch { - @Inject(method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V", at = @At("HEAD"), cancellable = true) + @Inject(method = "send(Lnet/minecraft/network/protocol/Packet;)V", at = @At("HEAD"), cancellable = true) public void onSendPacket(Packet<?> packet, CallbackInfo ci) { if (OutgoingPacketEvent.Companion.publish(new OutgoingPacketEvent(packet)).getCancelled()) { ci.cancel(); diff --git a/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java b/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java index 9a4626f..9ee271d 100644 --- a/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java @@ -3,26 +3,26 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.IsSlotProtectedEvent; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.screen.slot.Slot; -import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.inventory.ClickType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(ClientPlayerEntity.class) -public abstract class PlayerDropEventPatch extends PlayerEntity { - public PlayerDropEventPatch() { - super(null, null, 0, null); - } +@Mixin(LocalPlayer.class) +public abstract class PlayerDropEventPatch extends Player { + public PlayerDropEventPatch() { + super(null, null); + } - @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) - public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) { - Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0); - if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW)) { - cir.setReturnValue(false); - } - } + @Inject(method = "drop", at = @At("HEAD"), cancellable = true) + public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) { + Slot fakeSlot = new Slot(getInventory(), getInventory().getSelectedSlot(), 0, 0); + if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, ClickType.THROW, IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR)) { + cir.setReturnValue(false); + } + } } diff --git a/src/main/java/moe/nea/firmament/mixins/PropertySignatureIgnorePatch.java b/src/main/java/moe/nea/firmament/mixins/PropertySignatureIgnorePatch.java deleted file mode 100644 index e7331c5..0000000 --- a/src/main/java/moe/nea/firmament/mixins/PropertySignatureIgnorePatch.java +++ /dev/null @@ -1,36 +0,0 @@ - - -package moe.nea.firmament.mixins; - -import com.mojang.authlib.properties.Property; -import moe.nea.firmament.features.fixes.Fixes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.security.PublicKey; - -@Mixin(value = Property.class, remap = false) -public class PropertySignatureIgnorePatch { - @Inject(method = "isSignatureValid", cancellable = true, at = @At("HEAD"), remap = false) - public void onValidateSignature(PublicKey publicKey, CallbackInfoReturnable<Boolean> cir) { - if (Fixes.TConfig.INSTANCE.getFixUnsignedPlayerSkins()) { - cir.setReturnValue(true); - } - } - - @Inject(method = "signature", cancellable = true, at = @At("HEAD"), remap = false) - public void returnEmptySignatureInsteadOfNull(CallbackInfoReturnable<String> cir) { - if (Fixes.TConfig.INSTANCE.getFixUnsignedPlayerSkins()) { - cir.setReturnValue(""); - } - } - - @Inject(method = "hasSignature", cancellable = true, at = @At("HEAD"), remap = false) - public void onHasSignature(CallbackInfoReturnable<Boolean> cir) { - if (Fixes.TConfig.INSTANCE.getFixUnsignedPlayerSkins()) { - cir.setReturnValue(true); - } - } -} diff --git a/src/main/java/moe/nea/firmament/mixins/ResourceReloaderRegistrationPatch.java b/src/main/java/moe/nea/firmament/mixins/ResourceReloaderRegistrationPatch.java index 28fe3d9..a29cdc0 100644 --- a/src/main/java/moe/nea/firmament/mixins/ResourceReloaderRegistrationPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/ResourceReloaderRegistrationPatch.java @@ -2,9 +2,9 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.FinalizeResourceManagerEvent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.RunArgs; -import net.minecraft.resource.ReloadableResourceManagerImpl; +import net.minecraft.client.Minecraft; +import net.minecraft.client.main.GameConfig; +import net.minecraft.server.packs.resources.ReloadableResourceManager; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -12,14 +12,14 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(Minecraft.class) public class ResourceReloaderRegistrationPatch { @Shadow @Final - private ReloadableResourceManagerImpl resourceManager; + private ReloadableResourceManager resourceManager; - @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourcePackManager;createResourcePacks()Ljava/util/List;", shift = At.Shift.BEFORE)) - private void onBeforeResourcePackCreation(RunArgs args, CallbackInfo ci) { + @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/repository/PackRepository;openAllSelected()Ljava/util/List;", shift = At.Shift.BEFORE)) + private void onBeforeResourcePackCreation(GameConfig args, CallbackInfo ci) { FinalizeResourceManagerEvent.Companion.publish(new FinalizeResourceManagerEvent(this.resourceManager)); } } diff --git a/src/main/java/moe/nea/firmament/mixins/SaveCursorPositionPatch.java b/src/main/java/moe/nea/firmament/mixins/SaveCursorPositionPatch.java index fd3adca..3cfd0d6 100644 --- a/src/main/java/moe/nea/firmament/mixins/SaveCursorPositionPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/SaveCursorPositionPatch.java @@ -4,7 +4,7 @@ package moe.nea.firmament.mixins; import kotlin.Pair; import moe.nea.firmament.features.inventory.SaveCursorPosition; -import net.minecraft.client.Mouse; +import net.minecraft.client.MouseHandler; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -12,29 +12,29 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(Mouse.class) +@Mixin(MouseHandler.class) public class SaveCursorPositionPatch { - @Shadow - private double x; - - @Shadow - private double y; - - @Inject(method = "lockCursor", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Mouse;cursorLocked:Z")) - public void onLockCursor(CallbackInfo ci) { - SaveCursorPosition.saveCursorOriginal(x, y); - } - - @Inject(method = "lockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;getHandle()J")) - public void onLockCursorAfter(CallbackInfo ci) { - SaveCursorPosition.saveCursorMiddle(x, y); - } - - @Inject(method = "unlockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;getHandle()J")) - public void onUnlockCursor(CallbackInfo ci) { - Pair<Double, Double> cursorPosition = SaveCursorPosition.loadCursor(this.x, this.y); - if (cursorPosition == null) return; - this.x = cursorPosition.getFirst(); - this.y = cursorPosition.getSecond(); - } + @Shadow + private double xpos; + + @Shadow + private double ypos; + + @Inject(method = "grabMouse", at = @At(value = "HEAD")) + public void onLockCursor(CallbackInfo ci) { + SaveCursorPosition.saveCursorOriginal(xpos, ypos); + } + + @Inject(method = "grabMouse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getWindow()Lcom/mojang/blaze3d/platform/Window;", ordinal = 2)) + public void onLockCursorAfter(CallbackInfo ci) { + SaveCursorPosition.saveCursorMiddle(xpos, ypos); + } + + @Inject(method = "releaseMouse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getWindow()Lcom/mojang/blaze3d/platform/Window;", ordinal = 2)) + public void onUnlockCursor(CallbackInfo ci) { + Pair<Double, Double> cursorPosition = SaveCursorPosition.loadCursor(this.xpos, this.ypos); + if (cursorPosition == null) return; + this.xpos = cursorPosition.getFirst(); + this.ypos = cursorPosition.getSecond(); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/SaveOriginalCommandTreePacket.java b/src/main/java/moe/nea/firmament/mixins/SaveOriginalCommandTreePacket.java index 2f2f188..5974277 100644 --- a/src/main/java/moe/nea/firmament/mixins/SaveOriginalCommandTreePacket.java +++ b/src/main/java/moe/nea/firmament/mixins/SaveOriginalCommandTreePacket.java @@ -1,17 +1,17 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.features.chat.QuickCommands; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.game.ClientboundCommandsPacket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayNetworkHandler.class) +@Mixin(ClientPacketListener.class) public class SaveOriginalCommandTreePacket { - @Inject(method = "onCommandTree", at = @At(value = "RETURN")) - private void saveUnmodifiedCommandTree(CommandTreeS2CPacket packet, CallbackInfo ci) { + @Inject(method = "handleCommands", at = @At(value = "RETURN")) + private void saveUnmodifiedCommandTree(ClientboundCommandsPacket packet, CallbackInfo ci) { QuickCommands.INSTANCE.setLastReceivedTreePacket(packet); } } diff --git a/src/main/java/moe/nea/firmament/mixins/ScreenChangeEventPatch.java b/src/main/java/moe/nea/firmament/mixins/ScreenChangeEventPatch.java index 6d19405..37423f0 100644 --- a/src/main/java/moe/nea/firmament/mixins/ScreenChangeEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/ScreenChangeEventPatch.java @@ -5,8 +5,8 @@ package moe.nea.firmament.mixins; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import moe.nea.firmament.events.ScreenChangeEvent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -14,15 +14,15 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(Minecraft.class) public abstract class ScreenChangeEventPatch { @Shadow @Nullable - public Screen currentScreen; + public Screen screen; @Inject(method = "setScreen", at = @At("HEAD"), cancellable = true) public void onScreenChange(Screen screen, CallbackInfo ci, @Local(argsOnly = true) LocalRef<Screen> screenLocalRef) { - var event = new ScreenChangeEvent(currentScreen, screen); + var event = new ScreenChangeEvent(screen, screen); if (ScreenChangeEvent.Companion.publish(event).getCancelled()) { ci.cancel(); } else if (event.getOverrideScreen() != null) { diff --git a/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java b/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java new file mode 100644 index 0000000..edc8fd6 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java @@ -0,0 +1,34 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.events.HandledScreenKeyPressedEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Screen.class) +public class ScreenInputEvents { + @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) + public void onKeyPressed(KeyEvent input, CallbackInfoReturnable<Boolean> cir) { + if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent( + (Screen) (Object) this, + GenericInputAction.of(input), + InputModifiers.of(input))).getCancelled()) { + cir.setReturnValue(true); + } + } + + public boolean onMouseClicked$firmament(MouseButtonEvent click, boolean doubled) { + return HandledScreenKeyPressedEvent.Companion.publish( + new HandledScreenKeyPressedEvent((Screen) (Object) this, + GenericInputAction.mouse(click), InputModifiers.current())).getCancelled(); + } + + +} diff --git a/src/main/java/moe/nea/firmament/mixins/SlotClickEventPatch.java b/src/main/java/moe/nea/firmament/mixins/SlotClickEventPatch.java index 21e7899..db87f37 100644 --- a/src/main/java/moe/nea/firmament/mixins/SlotClickEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/SlotClickEventPatch.java @@ -5,11 +5,11 @@ import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import moe.nea.firmament.events.SlotClickEvent; -import net.minecraft.client.network.ClientPlayerInteractionManager; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ClickType; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -17,18 +17,19 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayerInteractionManager.class) +@Mixin(MultiPlayerGameMode.class) public class SlotClickEventPatch { - @Inject(method = "clickSlot", at = @At(value = "FIELD", target = "Lnet/minecraft/screen/ScreenHandler;slots:Lnet/minecraft/util/collection/DefaultedList;", opcode = Opcodes.GETFIELD)) - private void onSlotClickSaveSlot(int syncId, int slotId, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo ci, @Local ScreenHandler handler, @Share("slotContent") LocalRef<ItemStack> slotContent) { + @Inject(method = "handleInventoryMouseClick", at = @At(value = "FIELD", target = + "Lnet/minecraft/world/inventory/AbstractContainerMenu;slots:Lnet/minecraft/core/NonNullList;", opcode = Opcodes.GETFIELD)) + private void onSlotClickSaveSlot(int containerId, int slotId, int mouseButton, ClickType clickType, Player player, CallbackInfo ci, @Local AbstractContainerMenu handler, @Share("slotContent") LocalRef<ItemStack> slotContent) { if (0 <= slotId && slotId < handler.slots.size()) { - slotContent.set(handler.getSlot(slotId).getStack().copy()); + slotContent.set(handler.getSlot(slotId).getItem().copy()); } } - @Inject(method = "clickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V")) - private void onSlotClick(int syncId, int slotId, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo ci, @Local ScreenHandler handler, @Share("slotContent") LocalRef<ItemStack> slotContent) { + @Inject(method = "handleInventoryMouseClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V")) + private void onSlotClick(int syncId, int slotId, int button, ClickType actionType, Player player, CallbackInfo ci, @Local AbstractContainerMenu handler, @Share("slotContent") LocalRef<ItemStack> slotContent) { if (0 <= slotId && slotId < handler.slots.size()) { SlotClickEvent.Companion.publish(new SlotClickEvent( handler.getSlot(slotId), diff --git a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java index 06ecbd4..49661d0 100644 --- a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java +++ b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java @@ -3,51 +3,51 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.ChestInventoryUpdateEvent; import moe.nea.firmament.events.PlayerInventoryUpdate; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientCommonNetworkHandler; -import net.minecraft.client.network.ClientConnectionState; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.packet.s2c.play.InventoryS2CPacket; -import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; +import net.minecraft.client.multiplayer.CommonListenerCookie; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayNetworkHandler.class) -public abstract class SlotUpdateListener extends ClientCommonNetworkHandler { - protected SlotUpdateListener(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) { +@Mixin(ClientPacketListener.class) +public abstract class SlotUpdateListener extends ClientCommonPacketListenerImpl { + protected SlotUpdateListener(Minecraft client, Connection connection, CommonListenerCookie connectionState) { super(client, connection, connectionState); } @Inject( - method = "onScreenHandlerSlotUpdate", + method = "handleContainerSetSlot", at = @At(value = "TAIL")) private void onSingleSlotUpdate( - ScreenHandlerSlotUpdateS2CPacket packet, + ClientboundContainerSetSlotPacket packet, CallbackInfo ci) { - var player = this.client.player; + var player = this.minecraft.player; assert player != null; - if (packet.getSyncId() == 0) { - PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack())); - } else if (packet.getSyncId() == player.currentScreenHandler.syncId) { + if (packet.getContainerId() == 0) { + PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getItem())); + } else if (packet.getContainerId() == player.containerMenu.containerId) { ChestInventoryUpdateEvent.Companion.publish( - new ChestInventoryUpdateEvent.Single(packet.getSlot(), packet.getStack()) + new ChestInventoryUpdateEvent.Single(packet.getSlot(), packet.getItem()) ); } } - @Inject(method = "onInventory", + @Inject(method = "handleContainerContent", at = @At("TAIL")) - private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) { - var player = this.client.player; + private void onMultiSlotUpdate(ClientboundContainerSetContentPacket packet, CallbackInfo ci) { + var player = this.minecraft.player; assert player != null; - if (packet.getSyncId() == 0) { - PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.getContents())); - } else if (packet.getSyncId() == player.currentScreenHandler.syncId) { + if (packet.containerId() == 0) { + PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Multi(packet.items())); + } else if (packet.containerId() == player.containerMenu.containerId) { ChestInventoryUpdateEvent.Companion.publish( - new ChestInventoryUpdateEvent.Multi(packet.getContents()) + new ChestInventoryUpdateEvent.Multi(packet.items()) ); } } diff --git a/src/main/java/moe/nea/firmament/mixins/SoundReceiveEventPatch.java b/src/main/java/moe/nea/firmament/mixins/SoundReceiveEventPatch.java index 5c52d70..e685b0c 100644 --- a/src/main/java/moe/nea/firmament/mixins/SoundReceiveEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/SoundReceiveEventPatch.java @@ -1,30 +1,32 @@ package moe.nea.firmament.mixins; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import moe.nea.firmament.events.SoundReceiveEvent; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; -import net.minecraft.util.math.Vec3d; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.core.Holder; +import net.minecraft.sounds.SoundSource; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPlayNetworkHandler.class) +@Mixin(ClientPacketListener.class) public class SoundReceiveEventPatch { - @Inject(method = "onPlaySound", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/registry/entry/RegistryEntry;Lnet/minecraft/sound/SoundCategory;FFJ)V"), cancellable = true) - private void postEventWhenSoundIsPlayed(PlaySoundS2CPacket packet, CallbackInfo ci) { + @WrapWithCondition(method = "handleSoundEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;playSeededSound(Lnet/minecraft/world/entity/Entity;DDDLnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V")) + private boolean postEventWhenSoundIsPlayed(ClientLevel instance, @Nullable Entity source, double x, double y, double z, Holder<SoundEvent> sound, SoundSource category, float volume, float pitch, long seed) { var event = new SoundReceiveEvent( - packet.getSound(), - packet.getCategory(), - new Vec3d(packet.getX(), packet.getY(), packet.getZ()), - packet.getPitch(), - packet.getVolume(), - packet.getSeed() + sound, + category, + new Vec3(x,y,z), + pitch, + volume, + seed ); SoundReceiveEvent.Companion.publish(event); - if (event.getCancelled()) { - ci.cancel(); - } + return !event.getCancelled(); } } diff --git a/src/main/java/moe/nea/firmament/mixins/ToggleSprintPatch.java b/src/main/java/moe/nea/firmament/mixins/ToggleSprintPatch.java index 1acbf20..96d03bd 100644 --- a/src/main/java/moe/nea/firmament/mixins/ToggleSprintPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/ToggleSprintPatch.java @@ -3,16 +3,16 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.features.fixes.Fixes; -import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.KeyMapping; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(KeyBinding.class) +@Mixin(KeyMapping.class) public class ToggleSprintPatch { - @Inject(method = "isPressed", at = @At("HEAD"), cancellable = true) + @Inject(method = "isDown", at = @At("HEAD"), cancellable = true) public void onIsPressed(CallbackInfoReturnable<Boolean> cir) { - Fixes.INSTANCE.handleIsPressed((KeyBinding) (Object) this, cir); + Fixes.INSTANCE.handleIsPressed((KeyMapping) (Object) this, cir); } } diff --git a/src/main/java/moe/nea/firmament/mixins/TolerateFirmamentTolerateRegistryOwners.java b/src/main/java/moe/nea/firmament/mixins/TolerateFirmamentTolerateRegistryOwners.java index ac6f614..bc0ece5 100644 --- a/src/main/java/moe/nea/firmament/mixins/TolerateFirmamentTolerateRegistryOwners.java +++ b/src/main/java/moe/nea/firmament/mixins/TolerateFirmamentTolerateRegistryOwners.java @@ -1,16 +1,16 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.util.mc.TolerantRegistriesOps; -import net.minecraft.registry.entry.RegistryEntryOwner; +import net.minecraft.core.HolderOwner; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(RegistryEntryOwner.class) +@Mixin(HolderOwner.class) public interface TolerateFirmamentTolerateRegistryOwners<T> { - @Inject(method = "ownerEquals", at = @At("HEAD"), cancellable = true) - private void equalTolerantRegistryOwners(RegistryEntryOwner<T> other, CallbackInfoReturnable<Boolean> cir) { + @Inject(method = "canSerializeIn", at = @At("HEAD"), cancellable = true) + private void equalTolerantRegistryOwners(HolderOwner<T> other, CallbackInfoReturnable<Boolean> cir) { if (other instanceof TolerantRegistriesOps.TolerantOwner<?>) { cir.setReturnValue(true); } diff --git a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java index d4b8c9e..70ef9cb 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java @@ -3,16 +3,15 @@ package moe.nea.firmament.mixins; import moe.nea.firmament.events.WorldReadyEvent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.DownloadingTerrainScreen; +import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(Minecraft.class) public class WorldReadyEventPatch { - @Inject(method = "joinWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setWorld(Lnet/minecraft/client/world/ClientWorld;)V", shift = At.Shift.AFTER)) + @Inject(method = "setLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;updateLevelInEngines(Lnet/minecraft/client/multiplayer/ClientLevel;)V", shift = At.Shift.AFTER)) public void onClose(CallbackInfo ci) { WorldReadyEvent.Companion.publish(new WorldReadyEvent()); } diff --git a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java index 847fb4d..a875bcf 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java @@ -2,13 +2,18 @@ package moe.nea.firmament.mixins; -import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.blaze3d.buffers.GpuBufferSlice; import moe.nea.firmament.events.WorldRenderLastEvent; -import net.minecraft.client.render.*; -import net.minecraft.client.util.Handle; -import net.minecraft.client.util.ObjectAllocator; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.profiler.Profiler; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LevelTargetBundle; +import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.state.LevelRenderState; +import com.mojang.blaze3d.resource.ResourceHandle; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.util.profiling.ProfilerFiller; +import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -17,31 +22,30 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(WorldRenderer.class) +@Mixin(LevelRenderer.class) public abstract class WorldRenderLastEventPatch { @Shadow @Final - private BufferBuilderStorage bufferBuilders; + private RenderBuffers renderBuffers; @Shadow - @Final - private DefaultFramebufferSet framebufferSet; + protected abstract void checkPoseStack(PoseStack matrices); @Shadow - protected abstract void checkEmpty(MatrixStack matrices); + private int ticks; - @Inject(method = "method_62214", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V", shift = At.Shift.AFTER)) - public void onWorldRenderLast(Fog fog, RenderTickCounter tickCounter, Camera camera, Profiler profiler, Matrix4f matrix4f, Matrix4f matrix4f2, Handle handle, Handle handle2, Handle handle3, Handle handle4, boolean bl, Frustum frustum, Handle handle5, CallbackInfo ci) { - var imm = this.bufferBuilders.getEntityVertexConsumers(); - var stack = new MatrixStack(); + @Inject(method = "method_62214", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;pop()V", shift = At.Shift.AFTER)) + public void onWorldRenderLast(GpuBufferSlice gpuBufferSlice, LevelRenderState worldRenderState, ProfilerFiller profiler, Matrix4f matrix4f, ResourceHandle handle, ResourceHandle handle2, boolean bl, Frustum frustum, ResourceHandle handle3, ResourceHandle handle4, CallbackInfo ci) { + var imm = this.renderBuffers.bufferSource(); + var stack = new PoseStack(); // TODO: pre-cancel this event if F1 is active var event = new WorldRenderLastEvent( - stack, tickCounter, - camera, + stack, ticks, + worldRenderState.cameraRenderState, imm ); WorldRenderLastEvent.Companion.publish(event); - imm.draw(); - checkEmpty(stack); + imm.endBatch(); + checkPoseStack(stack); } } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java index 0a10046..cbd143d 100644 --- a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java @@ -1,13 +1,13 @@ package moe.nea.firmament.mixins.accessor; -import net.minecraft.client.network.AbstractClientPlayerEntity; -import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.multiplayer.PlayerInfo; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(AbstractClientPlayerEntity.class) +@Mixin(AbstractClientPlayer.class) public interface AccessorAbstractClientPlayerEntity { - @Accessor("playerListEntry") - void setPlayerListEntry_firmament(PlayerListEntry playerListEntry); + @Accessor("playerInfo") + void setPlayerListEntry_firmament(PlayerInfo playerListEntry); } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java index 72a72f0..0b986ef 100644 --- a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorChatHud.java @@ -1,14 +1,24 @@ package moe.nea.firmament.mixins.accessor; -import net.minecraft.client.gui.hud.ChatHud; -import net.minecraft.client.gui.hud.ChatHudLine; +import net.minecraft.client.gui.components.ChatComponent; +import net.minecraft.client.GuiMessage; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; import java.util.List; -@Mixin(ChatHud.class) +@Mixin(ChatComponent.class) public interface AccessorChatHud { - @Accessor("messages") - List<ChatHudLine> getMessages_firmament(); + @Accessor("allMessages") + List<GuiMessage> getMessages_firmament(); + + @Accessor("trimmedMessages") + List<GuiMessage.Line> getVisibleMessages_firmament(); + + @Accessor("chatScrollbarPos") + int getScrolledLines_firmament(); + + @Invoker("screenToChatY") + double toChatLineY_firmament(double y); } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorHandledScreen.java index 7ed04b1..5e0c669 100644 --- a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorHandledScreen.java @@ -1,41 +1,39 @@ - - package moe.nea.firmament.mixins.accessor; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.screen.slot.Slot; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.Slot; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(HandledScreen.class) +@Mixin(AbstractContainerScreen.class) public interface AccessorHandledScreen { - @Accessor("focusedSlot") + @Accessor("hoveredSlot") @Nullable - Slot getFocusedSlot_Firmament(); + Slot getFocusedSlot_Firmament(); - @Accessor("backgroundWidth") + @Accessor("imageWidth") int getBackgroundWidth_Firmament(); - @Accessor("backgroundWidth") + @Accessor("imageWidth") void setBackgroundWidth_Firmament(int newBackgroundWidth); - @Accessor("backgroundHeight") + @Accessor("imageHeight") int getBackgroundHeight_Firmament(); - @Accessor("backgroundHeight") + @Accessor("imageHeight") void setBackgroundHeight_Firmament(int newBackgroundHeight); - @Accessor("x") + @Accessor("leftPos") int getX_Firmament(); - @Accessor("x") + @Accessor("leftPos") void setX_Firmament(int newX); - @Accessor("y") + @Accessor("topPos") int getY_Firmament(); - @Accessor("y") + @Accessor("topPos") void setY_Firmament(int newY); } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorNbtComponent.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorNbtComponent.java new file mode 100644 index 0000000..672badf --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorNbtComponent.java @@ -0,0 +1,12 @@ +package moe.nea.firmament.mixins.accessor; + +import net.minecraft.world.item.component.CustomData; +import net.minecraft.nbt.CompoundTag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(CustomData.class) +public interface AccessorNbtComponent { + @Accessor("tag") + CompoundTag getUnsafeNbt_firmament(); +} diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorPlayerListHud.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorPlayerListHud.java new file mode 100644 index 0000000..58c9ad9 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorPlayerListHud.java @@ -0,0 +1,31 @@ +package moe.nea.firmament.mixins.accessor; + +import net.minecraft.client.gui.components.PlayerTabOverlay; +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.Comparator; +import java.util.List; + +@Mixin(PlayerTabOverlay.class) +public interface AccessorPlayerListHud { + + @Accessor("PLAYER_COMPARATOR") + static Comparator<PlayerInfo> getEntryOrdering() { + throw new AssertionError(); + } + + @Invoker("getPlayerInfos") + List<PlayerInfo> collectPlayerEntries_firmament(); + + @Accessor("footer") + @Nullable Component getFooter_firmament(); + + @Accessor("header") + @Nullable Component getHeader_firmament(); + +} diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java new file mode 100644 index 0000000..0a75ea1 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java @@ -0,0 +1,12 @@ +package moe.nea.firmament.mixins.accessor; + +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(AbstractContainerMenu.class) +public interface AccessorScreenHandler { + @Accessor("menuType") + MenuType<?> getType_firmament(); +} diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorWorldRenderer.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorWorldRenderer.java new file mode 100644 index 0000000..9164af0 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorWorldRenderer.java @@ -0,0 +1,17 @@ +package moe.nea.firmament.mixins.accessor; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.server.level.BlockDestructionProgress; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.SortedSet; + +@Mixin(LevelRenderer.class) +public interface AccessorWorldRenderer { + @Accessor("destructionProgress") + @NotNull + Long2ObjectMap<SortedSet<BlockDestructionProgress>> getBlockBreakingProgressions_firmament(); +} diff --git a/src/main/java/moe/nea/firmament/mixins/customgui/OriginalSlotCoords.java b/src/main/java/moe/nea/firmament/mixins/customgui/OriginalSlotCoords.java index c705625..203b87e 100644 --- a/src/main/java/moe/nea/firmament/mixins/customgui/OriginalSlotCoords.java +++ b/src/main/java/moe/nea/firmament/mixins/customgui/OriginalSlotCoords.java @@ -2,7 +2,7 @@ package moe.nea.firmament.mixins.customgui; import moe.nea.firmament.util.customgui.CoordRememberingSlot; -import net.minecraft.screen.slot.Slot; +import net.minecraft.world.inventory.Slot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; diff --git a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java index 6e1090a..b0fbbe1 100644 --- a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java @@ -4,17 +4,21 @@ package moe.nea.firmament.mixins.customgui; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; import moe.nea.firmament.events.HandledScreenKeyReleasedEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; import moe.nea.firmament.util.customgui.CoordRememberingSlot; import moe.nea.firmament.util.customgui.CustomGui; import moe.nea.firmament.util.customgui.HasCustomGui; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.slot.Slot; -import net.minecraft.text.Text; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.input.CharacterEvent; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -25,19 +29,19 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(HandledScreen.class) -public class PatchHandledScreen<T extends ScreenHandler> extends Screen implements HasCustomGui { +@Mixin(AbstractContainerScreen.class) +public class PatchHandledScreen<T extends AbstractContainerMenu> extends Screen implements HasCustomGui { @Shadow @Final - protected T handler; + protected T menu; @Shadow - protected int x; + protected int leftPos; @Shadow - protected int y; + protected int topPos; @Shadow - protected int backgroundHeight; + protected int imageHeight; @Shadow - protected int backgroundWidth; + protected int imageWidth; @Unique public CustomGui override; @Unique @@ -47,7 +51,7 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen @Unique private int originalBackgroundHeight; - protected PatchHandledScreen(Text title) { + protected PatchHandledScreen(Component title) { super(title); } @@ -60,12 +64,12 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen @Override public void setCustomGui_Firmament(@Nullable CustomGui gui) { if (this.override != null) { - backgroundHeight = originalBackgroundHeight; - backgroundWidth = originalBackgroundWidth; + imageHeight = originalBackgroundHeight; + imageWidth = originalBackgroundWidth; } if (gui != null) { - originalBackgroundHeight = backgroundHeight; - originalBackgroundWidth = backgroundWidth; + originalBackgroundHeight = imageHeight; + originalBackgroundWidth = imageWidth; } this.override = gui; } @@ -74,14 +78,17 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen return override != null && override.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); } - public boolean keyReleased_firmament(int keyCode, int scanCode, int modifiers) { - if (HandledScreenKeyReleasedEvent.Companion.publish(new HandledScreenKeyReleasedEvent((HandledScreen<?>) (Object) this, keyCode, scanCode, modifiers)).getCancelled()) + public boolean keyReleased_firmament(KeyEvent input) { + if (HandledScreenKeyReleasedEvent.Companion.publish(new HandledScreenKeyReleasedEvent( + (AbstractContainerScreen<?>) (Object) this, + GenericInputAction.of(input), + InputModifiers.of(input))).getCancelled()) return true; - return override != null && override.keyReleased(keyCode, scanCode, modifiers); + return override != null && override.keyReleased(input); } - public boolean charTyped_firmament(char chr, int modifiers) { - return override != null && override.charTyped(chr, modifiers); + public boolean charTyped_firmament(CharacterEvent input) { + return override != null && override.charTyped(input); } @Inject(method = "init", at = @At("TAIL")) @@ -91,19 +98,19 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } } - @Inject(method = "drawForeground", at = @At("HEAD"), cancellable = true) - private void onDrawForeground(DrawContext context, int mouseX, int mouseY, CallbackInfo ci) { + @Inject(method = "renderLabels", at = @At("HEAD"), cancellable = true) + private void onDrawForeground(GuiGraphics context, int mouseX, int mouseY, CallbackInfo ci) { if (override != null && !override.shouldDrawForeground()) ci.cancel(); } @WrapOperation( - method = "drawSlots", + method = "renderSlots", at = @At( value = "INVOKE", - target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) - private void beforeSlotRender(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) { + target = "Lnet/minecraft/client/gui/screens/inventory/AbstractContainerScreen;renderSlot(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/inventory/Slot;)V")) + private void beforeSlotRender(AbstractContainerScreen instance, GuiGraphics context, Slot slot, Operation<Void> original) { if (override != null) { override.beforeSlotRender(context, slot); } @@ -113,31 +120,33 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } } - @Inject(method = "isClickOutsideBounds", at = @At("HEAD"), cancellable = true) - public void onIsClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button, CallbackInfoReturnable<Boolean> cir) { + @Inject(method = "hasClickedOutside", at = @At("HEAD"), cancellable = true) + public void onIsClickOutsideBounds( + double mouseX, double mouseY, int left, int top, + CallbackInfoReturnable<Boolean> cir) { if (override != null) { cir.setReturnValue(override.isClickOutsideBounds(mouseX, mouseY)); } } - @Inject(method = "isPointWithinBounds", at = @At("HEAD"), cancellable = true) + @Inject(method = "isHovering(IIIIDD)Z", at = @At("HEAD"), cancellable = true) public void onIsPointWithinBounds(int x, int y, int width, int height, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) { if (override != null) { - cir.setReturnValue(override.isPointWithinBounds(x + this.x, y + this.y, width, height, pointX, pointY)); + cir.setReturnValue(override.isPointWithinBounds(x + this.leftPos, y + this.topPos, width, height, pointX, pointY)); } } - @Inject(method = "isPointOverSlot", at = @At("HEAD"), cancellable = true) + @Inject(method = "isHovering(Lnet/minecraft/world/inventory/Slot;DD)Z", at = @At("HEAD"), cancellable = true) public void onIsPointOverSlot(Slot slot, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) { if (override != null) { - cir.setReturnValue(override.isPointOverSlot(slot, this.x, this.y, pointX, pointY)); + cir.setReturnValue(override.isPointOverSlot(slot, this.leftPos, this.topPos, pointX, pointY)); } } - @Inject(method = "render", at = @At("HEAD")) - public void moveSlots(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + @Inject(method = "renderBackground", at = @At("HEAD")) + public void moveSlots(GuiGraphics context, int mouseX, int mouseY, float delta, CallbackInfo ci) { if (override != null) { - for (Slot slot : handler.slots) { + for (Slot slot : menu.slots) { if (!hasRememberedSlots) { ((CoordRememberingSlot) slot).rememberCoords_firmament(); } @@ -146,7 +155,7 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen hasRememberedSlots = true; } else { if (hasRememberedSlots) { - for (Slot slot : handler.slots) { + for (Slot slot : menu.slots) { ((CoordRememberingSlot) slot).restoreCoords_firmament(); } hasRememberedSlots = false; @@ -154,7 +163,7 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } } - @Inject(at = @At("HEAD"), method = "close", cancellable = true) + @Inject(at = @At("HEAD"), method = "onClose", cancellable = true) private void onVoluntaryExit(CallbackInfo ci) { if (override != null) { if (!override.onVoluntaryExit()) @@ -162,8 +171,8 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen } } - @WrapWithCondition(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/DrawContext;FII)V")) - public boolean preventDrawingBackground(HandledScreen instance, DrawContext drawContext, float delta, int mouseX, int mouseY) { + @WrapWithCondition(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/AbstractContainerScreen;renderBg(Lnet/minecraft/client/gui/GuiGraphics;FII)V")) + public boolean preventDrawingBackground(AbstractContainerScreen instance, GuiGraphics drawContext, float delta, int mouseX, int mouseY) { if (override != null) { override.render(drawContext, delta, mouseX, mouseY); } @@ -172,28 +181,27 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen @WrapOperation( method = "mouseClicked", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z")) - public boolean overrideMouseClicks(HandledScreen instance, double mouseX, double mouseY, int button, - Operation<Boolean> original) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;mouseClicked(Lnet/minecraft/client/input/MouseButtonEvent;Z)Z")) + public boolean overrideMouseClicks(AbstractContainerScreen instance, MouseButtonEvent click, boolean doubled, Operation<Boolean> original) { if (override != null) { - if (override.mouseClick(mouseX, mouseY, button)) + if (override.mouseClick(click, doubled)) return true; } - return original.call(instance, mouseX, mouseY, button); + return original.call(instance, click, doubled); } @Inject(method = "mouseDragged", at = @At("HEAD"), cancellable = true) - public void overrideMouseDrags(double mouseX, double mouseY, int button, double deltaX, double deltaY, CallbackInfoReturnable<Boolean> cir) { + public void overrideMouseDrags(MouseButtonEvent click, double offsetX, double offsetY, CallbackInfoReturnable<Boolean> cir) { if (override != null) { - if (override.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) + if (override.mouseDragged(click, offsetX, offsetY)) cir.setReturnValue(true); } } @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) - private void overrideKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) { + private void overrideKeyPressed(KeyEvent input, CallbackInfoReturnable<Boolean> cir) { if (override != null) { - if (override.keyPressed(keyCode, scanCode, modifiers)) { + if (override.keyPressed(input)) { cir.setReturnValue(true); } } @@ -203,9 +211,9 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen @Inject( method = "mouseReleased", at = @At("HEAD"), cancellable = true) - public void overrideMouseReleases(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { + public void overrideMouseReleases(MouseButtonEvent click, CallbackInfoReturnable<Boolean> cir) { if (override != null) { - if (override.mouseReleased(mouseX, mouseY, button)) + if (override.mouseReleased(click)) cir.setReturnValue(true); } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java b/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java index a15d825..2744fb4 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/DisableCommonPacketWarnings.java @@ -2,9 +2,9 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.packet.CustomPayload; -import net.minecraft.util.Identifier; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -14,27 +14,27 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Objects; -@Mixin(ClientPlayNetworkHandler.class) +@Mixin(ClientPacketListener.class) public class DisableCommonPacketWarnings { - @Inject(method = "warnOnUnknownPayload", at = @At("HEAD"), cancellable = true) - public void onCustomPacketError(CustomPayload customPayload, CallbackInfo ci) { - if (Objects.equals(customPayload.getId(), Identifier.of("badlion", "mods"))) { + @Inject(method = "handleUnknownCustomPayload", at = @At("HEAD"), cancellable = true) + public void onCustomPacketError(CustomPacketPayload customPayload, CallbackInfo ci) { + if (Objects.equals(customPayload.type(), ResourceLocation.fromNamespaceAndPath("badlion", "mods"))) { ci.cancel(); } } - @Redirect(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) + @Redirect(method = "handleSetEntityPassengersPacket", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) public void onUnknownPassenger(Logger instance, String s) { // Ignore passenger data for unknown entities, since HyPixel just sends a lot of those. } - @Redirect(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) + @Redirect(method = "handleSetPlayerTeamPacket", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) public void onOnTeam(Logger instance, String s, Object[] objects) { // Ignore data for unknown teams, since HyPixel just sends a lot of invalid team data. } - @Redirect(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) + @Redirect(method = "handlePlayerInfoUpdate", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) public void onOnPlayerList(Logger instance, String s, Object o, Object o2) { // Ignore invalid player info, since HyPixel just sends a lot of invalid player info } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/DisableInvalidFishingHook.java b/src/main/java/moe/nea/firmament/mixins/devenv/DisableInvalidFishingHook.java index 689a757..ffcfefa 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/DisableInvalidFishingHook.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/DisableInvalidFishingHook.java @@ -2,15 +2,15 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.world.entity.projectile.FishingHook; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -@Mixin(FishingBobberEntity.class) +@Mixin(FishingHook.class) public class DisableInvalidFishingHook { - @Redirect(method = "onSpawnPacket", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) + @Redirect(method = "recreateFromPacket", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) public void onOnSpawnPacket(Logger instance, String s, Object o, Object o1) { // Don't warn for broken fishing hooks, since HyPixel sends a bunch of those } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java b/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java index ef8c9eb..849525f 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/EarlyInstantiateTranslations.java @@ -1,19 +1,19 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.text.TranslatableTextContent; +import net.minecraft.network.chat.contents.TranslatableContents; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(TranslatableTextContent.class) +@Mixin(TranslatableContents.class) public abstract class EarlyInstantiateTranslations { @Shadow - protected abstract void updateTranslations(); + protected abstract void decompose(); @Inject(method = "<init>", at = @At("TAIL")) private void onInit(String key, String fallback, Object[] args, CallbackInfo ci) { - updateTranslations(); + decompose(); } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java index 6620b47..c71f337 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyCloser.java @@ -1,7 +1,7 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.client.util.Window; +import com.mojang.blaze3d.platform.Window; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java index fac0688..cc04493 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/IdentifyStopperPatch.java @@ -1,16 +1,21 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.client.MinecraftClient; +import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(MinecraftClient.class) +@Mixin(Minecraft.class) public class IdentifyStopperPatch { - @Inject(method = "scheduleStop", at = @At("HEAD")) - private void onStop(CallbackInfo ci) { - Thread.dumpStack(); - } + @Shadow + private volatile boolean running; + + @Inject(method = "stop", at = @At("HEAD")) + private void onStop(CallbackInfo ci) { + if (this.running) + Thread.dumpStack(); + } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/MixinKeyboard.java b/src/main/java/moe/nea/firmament/mixins/devenv/MixinKeyboard.java index d7b6cc3..7d5fc80 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/MixinKeyboard.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/MixinKeyboard.java @@ -3,18 +3,18 @@ package moe.nea.firmament.mixins.devenv; import moe.nea.firmament.features.debug.DeveloperFeatures; -import net.minecraft.client.Keyboard; -import net.minecraft.client.MinecraftClient; +import net.minecraft.client.KeyboardHandler; +import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import java.util.concurrent.CompletableFuture; -@Mixin(Keyboard.class) +@Mixin(KeyboardHandler.class) public class MixinKeyboard { - @Redirect(method = "processF3", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;reloadResources()Ljava/util/concurrent/CompletableFuture;")) - public CompletableFuture<Void> redirectReloadResources(MinecraftClient instance) { + @Redirect(method = "handleDebugKeys", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;reloadResourcePacks()Ljava/util/concurrent/CompletableFuture;")) + public CompletableFuture<Void> redirectReloadResources(Minecraft instance) { return DeveloperFeatures.hookOnBeforeResourceReload(instance); } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/MixinScoreboard.java b/src/main/java/moe/nea/firmament/mixins/devenv/MixinScoreboard.java index 34a733c..e39fe35 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/MixinScoreboard.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/MixinScoreboard.java @@ -2,7 +2,7 @@ package moe.nea.firmament.mixins.devenv; -import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.world.scores.Scoreboard; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(Scoreboard.class) public class MixinScoreboard { - @Redirect(method = "addTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + @Redirect(method = {"addPlayerTeam", "addPlayerToTeam"}, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) public void onExistingteam(Logger instance, String s, Object o) { // Ignore creations of existing teams } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/WarnForUnknownCustomPayloadSends.java b/src/main/java/moe/nea/firmament/mixins/devenv/WarnForUnknownCustomPayloadSends.java index 6d44e29..9a96df2 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/WarnForUnknownCustomPayloadSends.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/WarnForUnknownCustomPayloadSends.java @@ -2,17 +2,17 @@ package moe.nea.firmament.mixins.devenv; import moe.nea.firmament.Firmament; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.UnknownCustomPayload; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.DiscardedPayload; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(UnknownCustomPayload.class) +@Mixin(DiscardedPayload.class) public class WarnForUnknownCustomPayloadSends { @Inject(method = "method_56493", at = @At("HEAD")) - private static void warn(UnknownCustomPayload value, PacketByteBuf buf, CallbackInfo ci) { + private static void warn(DiscardedPayload value, FriendlyByteBuf buf, CallbackInfo ci) { Firmament.INSTANCE.getLogger().warn("Unknown custom payload is being sent: {}", value); } } diff --git a/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java b/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java index 33840c1..e513a97 100644 --- a/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java +++ b/src/main/java/moe/nea/firmament/mixins/devenv/WarnOnMissingTranslations.java @@ -2,8 +2,8 @@ package moe.nea.firmament.mixins.devenv; import moe.nea.firmament.features.debug.DeveloperFeatures; import moe.nea.firmament.util.MC; -import net.minecraft.client.resource.language.TranslationStorage; -import net.minecraft.text.Text; +import net.minecraft.client.resources.language.ClientLanguage; +import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -14,15 +14,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Set; import java.util.TreeSet; -@Mixin(TranslationStorage.class) +@Mixin(ClientLanguage.class) public abstract class WarnOnMissingTranslations { @Shadow - public abstract boolean hasTranslation(String key); + public abstract boolean has(String key); @Unique private final Set<String> missingTranslations = new TreeSet<>(); - @Inject(method = "get", at = @At("HEAD")) + @Inject(method = "getOrDefault", at = @At("HEAD")) private void onGetTranslationKey(String key, String fallback, CallbackInfoReturnable<String> cir) { warnForMissingTranslation(key); } @@ -30,9 +30,9 @@ public abstract class WarnOnMissingTranslations { @Unique private void warnForMissingTranslation(String key) { if (!key.contains("firmament")) return; - if (hasTranslation(key)) return; + if (has(key)) return; if (!missingTranslations.add(key)) return; - MC.INSTANCE.sendChat(Text.literal("Missing firmament translation: " + key)); + MC.INSTANCE.sendChat(Component.literal("Missing firmament translation: " + key)); DeveloperFeatures.hookMissingTranslations(missingTranslations); } } diff --git a/src/main/java/moe/nea/firmament/mixins/feature/DisableSlotHighlights.java b/src/main/java/moe/nea/firmament/mixins/feature/DisableSlotHighlights.java new file mode 100644 index 0000000..475a5bf --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/feature/DisableSlotHighlights.java @@ -0,0 +1,25 @@ +package moe.nea.firmament.mixins.feature; + +import moe.nea.firmament.features.fixes.Fixes; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.inventory.Slot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Slot.class) +public abstract class DisableSlotHighlights { + @Shadow + public abstract ItemStack getItem(); + + @Inject(method = "isHighlightable", at = @At("HEAD"), cancellable = true) + private void dontHighlight(CallbackInfoReturnable<Boolean> cir) { + if (!Fixes.TConfig.INSTANCE.getHideSlotHighlights()) return; + var display = getItem().get(DataComponents.TOOLTIP_DISPLAY); + if (display != null && display.hideTooltip()) + cir.setReturnValue(false); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeFeatureRenderer.java b/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeFeatureRenderer.java new file mode 100644 index 0000000..7852fc3 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeFeatureRenderer.java @@ -0,0 +1,49 @@ +package moe.nea.firmament.mixins.feature.devcosmetics; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import kotlin.Unit; +import moe.nea.firmament.features.misc.CustomCapes; +import net.minecraft.client.model.Model; +import net.minecraft.client.renderer.RenderType; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.entity.layers.CapeLayer; +import net.minecraft.client.renderer.entity.layers.RenderLayer; +import net.minecraft.client.renderer.entity.RenderLayerParent; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.PlayerModel; +import net.minecraft.client.renderer.entity.state.AvatarRenderState; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.world.entity.player.PlayerSkin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(CapeLayer.class) +public abstract class CustomCapeFeatureRenderer extends RenderLayer<AvatarRenderState, PlayerModel> { + public CustomCapeFeatureRenderer(RenderLayerParent<AvatarRenderState, PlayerModel> context) { + super(context); + } + + @WrapOperation( + method = "submit(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/AvatarRenderState;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/SubmitNodeCollector;submitModel(Lnet/minecraft/client/model/Model;Ljava/lang/Object;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/RenderType;IIILnet/minecraft/client/renderer/feature/ModelFeatureRenderer$CrumblingOverlay;)V") + ) + private void onRender(SubmitNodeCollector instance, Model model, Object o, PoseStack matrixStack, RenderType renderLayer, int light, int overlay, int outlineColor, ModelFeatureRenderer.CrumblingOverlay crumblingOverlayCommand, Operation<Void> original, + @Local(argsOnly = true) AvatarRenderState playerEntityRenderState, @Local PlayerSkin skinTextures) { + // TODO: 1.21.10 custom capes by pre rendering the texture id. this is more viable on this version i am fairly sure, without clogging up all of the cached image render layers +// CustomCapes.render( +// playerEntityRenderState, +// vertexConsumer, +// RenderLayer.getEntitySolid(skinTextures.cape().id()), +// vertexConsumerProvider, +// matrixStack, +// updatedConsumer -> { +// original.call(instance, matrixStack, updatedConsumer, light, overlay, outlineColor); +// return Unit.INSTANCE; +// }); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeStorage.java b/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeStorage.java new file mode 100644 index 0000000..dc933ce --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/feature/devcosmetics/CustomCapeStorage.java @@ -0,0 +1,23 @@ +package moe.nea.firmament.mixins.feature.devcosmetics; + +import moe.nea.firmament.features.misc.CustomCapes; +import net.minecraft.client.renderer.entity.state.AvatarRenderState; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(AvatarRenderState.class) +public class CustomCapeStorage implements CustomCapes.CapeStorage { + @Unique + CustomCapes.CustomCape customCape; + + @Override + public CustomCapes.@Nullable CustomCape getCape_firmament() { + return customCape; + } + + @Override + public void setCape_firmament(CustomCapes.@Nullable CustomCape customCape) { + this.customCape = customCape; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/IncreaseStackLimitSizeInDrawContext.java b/src/main/java/moe/nea/firmament/mixins/render/IncreaseStackLimitSizeInDrawContext.java new file mode 100644 index 0000000..2352dfa --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/IncreaseStackLimitSizeInDrawContext.java @@ -0,0 +1,20 @@ +package moe.nea.firmament.mixins.render; + +import net.minecraft.client.gui.GuiGraphics; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(GuiGraphics.class) +public class IncreaseStackLimitSizeInDrawContext { + // [22:00:57] [Render thread/ERROR] (Minecraft) Couldn't compile program for pipeline firmament:gui_textured_overlay_tris_circle: + // net.minecraft.client.gl.ShaderLoader$LoadException: Error encountered when linking program containing + // VS minecraft:core/position_tex_color and FS firmament:circle_discard_color. + // Log output: error: declarations for uniform `ColorModulator` are inside block `DynamicTransforms` and outside a block + @ModifyArg( + method = "<init>(Lnet/minecraft/client/Minecraft;Lnet/minecraft/client/gui/render/state/GuiRenderState;)V", + at = @At(value = "INVOKE", target = "Lorg/joml/Matrix3x2fStack;<init>(I)V")) + private static int increaseStackSize(int stackSize) { + return Math.max(stackSize, 48); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/ChangeColorOfLivingEntities.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/ChangeColorOfLivingEntities.java new file mode 100644 index 0000000..53353e2 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/ChangeColorOfLivingEntities.java @@ -0,0 +1,65 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; +import net.minecraft.client.renderer.state.CameraRenderState; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * Applies various rendering modifications from {@link EntityRenderTintEvent} + */ +@Mixin(LivingEntityRenderer.class) +public class ChangeColorOfLivingEntities<T extends LivingEntity, S extends LivingEntityRenderState, M extends EntityModel<? super S>> { + @ModifyReturnValue(method = "getModelTint", at = @At("RETURN")) + private int changeColor(int original, @Local(argsOnly = true) S state) { + var tintState = EntityRenderTintEvent.HasTintRenderState.cast(state); + if (tintState.getHasTintOverride_firmament()) + return tintState.getTint_firmament(); + return original; + } + + @ModifyArg( + method = "getOverlayCoords", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/OverlayTexture;u(F)I"), + allow = 1 + ) + private static float modifyLightOverlay(float originalWhiteOffset, @Local(argsOnly = true) LivingEntityRenderState state) { + var tintState = EntityRenderTintEvent.HasTintRenderState.cast(state); + if (tintState.getHasTintOverride_firmament() || tintState.getOverlayTexture_firmament() != null) { + return 1F; // TODO: add interpolation percentage to render state extension + } + return originalWhiteOffset; + } + + @Inject(method = "submit(Lnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/state/CameraRenderState;)V", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;popPose()V")) + private void afterRender(S livingEntityRenderState, PoseStack matrixStack, SubmitNodeCollector orderedRenderCommandQueue, CameraRenderState cameraRenderState, CallbackInfo ci) { +// var tintState = EntityRenderTintEvent.HasTintRenderState.cast(livingEntityRenderState); +// var overlayTexture = tintState.getOverlayTexture_firmament(); +// if (overlayTexture != null && vertexConsumerProvider instanceof VertexConsumerProvider.Immediate imm) { +// imm.drawCurrentLayer(); +// } +// EntityRenderTintEvent.overlayOverride = null; + // TODO: 1.21.10 + } + + @Inject(method = "submit(Lnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/state/CameraRenderState;)V", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;pushPose()V")) + private void beforeRender(S livingEntityRenderState, PoseStack matrixStack, SubmitNodeCollector orderedRenderCommandQueue, CameraRenderState cameraRenderState, CallbackInfo ci) { + var tintState = EntityRenderTintEvent.HasTintRenderState.cast(livingEntityRenderState); + var overlayTexture = tintState.getOverlayTexture_firmament(); + if (overlayTexture != null) { + EntityRenderTintEvent.overlayOverride = overlayTexture; + } + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/EntityRenderStateTint.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/EntityRenderStateTint.java new file mode 100644 index 0000000..1745fc9 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/EntityRenderStateTint.java @@ -0,0 +1,55 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import moe.nea.firmament.events.EntityRenderTintEvent; +import moe.nea.firmament.util.render.TintedOverlayTexture; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(EntityRenderState.class) +public class EntityRenderStateTint implements EntityRenderTintEvent.HasTintRenderState { + @Unique + int tint = -1; + @Unique + TintedOverlayTexture overlayTexture; + @Unique + boolean hasTintOverride = false; + + @Override + public int getTint_firmament() { + return tint; + } + + @Override + public void setTint_firmament(int i) { + tint = i; + hasTintOverride = true; + } + + @Override + public boolean getHasTintOverride_firmament() { + return hasTintOverride; + } + + @Override + public void setHasTintOverride_firmament(boolean b) { + hasTintOverride = b; + } + + @Override + public void reset_firmament() { + hasTintOverride = false; + overlayTexture = null; + } + + @Override + public @Nullable TintedOverlayTexture getOverlayTexture_firmament() { + return overlayTexture; + } + + @Override + public void setOverlayTexture_firmament(@Nullable TintedOverlayTexture tintedOverlayTexture) { + this.overlayTexture = tintedOverlayTexture; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/InjectIntoRenderState.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/InjectIntoRenderState.java new file mode 100644 index 0000000..c84dbb6 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/InjectIntoRenderState.java @@ -0,0 +1,30 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * Dispatches {@link EntityRenderTintEvent} to collect additional render state used by {@link ChangeColorOfLivingEntities} + */ +@Mixin(EntityRenderer.class) +public class InjectIntoRenderState<T extends Entity, S extends EntityRenderState> { + + @Inject( + method = "extractRenderState", + at = @At("RETURN")) + private void onUpdateRenderState(T entity, S state, float tickDelta, CallbackInfo ci) { + var renderState = EntityRenderTintEvent.HasTintRenderState.cast(state); + renderState.reset_firmament(); + var tintEvent = new EntityRenderTintEvent( + entity, + renderState + ); + EntityRenderTintEvent.Companion.publish(tintEvent); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/ReplaceOverlayTexture.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/ReplaceOverlayTexture.java new file mode 100644 index 0000000..ef8b371 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/ReplaceOverlayTexture.java @@ -0,0 +1,24 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.RenderType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +/** + * Replaces the overlay texture used by rendering with the override specified in {@link EntityRenderTintEvent#overlayOverride} + */ +@Mixin(RenderType.OverlayStateShard.class) +public class ReplaceOverlayTexture { + @ModifyExpressionValue( + method = {"method_23555", "method_23556"}, + expect = 2, + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;overlayTexture()Lnet/minecraft/client/renderer/texture/OverlayTexture;")) + private static OverlayTexture replaceOverlayTexture(OverlayTexture original) { + if (EntityRenderTintEvent.overlayOverride != null) + return EntityRenderTintEvent.overlayOverride; + return original; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableEquipmentRenderer.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableEquipmentRenderer.java new file mode 100644 index 0000000..0cceea3 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableEquipmentRenderer.java @@ -0,0 +1,34 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.layers.EquipmentLayerRenderer; +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +/** + * Patch to make {@link EquipmentLayerRenderer} use a {@link RenderType} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified. + */ +@Mixin(EquipmentLayerRenderer.class) +public class UseOverlayableEquipmentRenderer { + @WrapOperation(method = "renderLayers(Lnet/minecraft/client/resources/model/EquipmentClientInfo$LayerType;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/client/model/Model;Ljava/lang/Object;Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/resources/ResourceLocation;II)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderType;armorCutoutNoCull(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType;")) + private RenderType replace(ResourceLocation texture, Operation<RenderType> original) { + if (EntityRenderTintEvent.overlayOverride != null) + return RenderType.entityTranslucent(texture); + return original.call(texture); + } + + @ModifyExpressionValue(method = "renderLayers(Lnet/minecraft/client/resources/model/EquipmentClientInfo$LayerType;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/client/model/Model;Ljava/lang/Object;Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/resources/ResourceLocation;II)V", + at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/OverlayTexture;NO_OVERLAY:I")) + private int replaceUvIndex(int original) { + if (EntityRenderTintEvent.overlayOverride != null) + return OverlayTexture.pack(15, 10); // TODO: store this info in a global alongside overlayOverride + return original; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableHeadFeatureRenderer.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableHeadFeatureRenderer.java new file mode 100644 index 0000000..a867c81 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableHeadFeatureRenderer.java @@ -0,0 +1,26 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.layers.CustomHeadLayer; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +/** + * Patch to make {@link CustomHeadLayer} use a {@link RenderType} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified. + * @see UseOverlayableItemRenderer + */ +@Mixin(CustomHeadLayer.class) +public class UseOverlayableHeadFeatureRenderer { + + @ModifyExpressionValue(method = "submit(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;FF)V", + at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/OverlayTexture;NO_OVERLAY:I", opcode = Opcodes.GETSTATIC)) + private int replaceUvIndex(int original) { + if (EntityRenderTintEvent.overlayOverride != null) + return OverlayTexture.pack(15, 10); // TODO: store this info in a global alongside overlayOverride + return original; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableItemRenderer.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableItemRenderer.java new file mode 100644 index 0000000..dcac77c --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableItemRenderer.java @@ -0,0 +1,26 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.item.ItemStackRenderState; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +/** + * Patch to make {@link ItemStackRenderState} use a {@link RenderType} that allows uses Minecraft's overlay texture. + * + * @see UseOverlayableHeadFeatureRenderer + */ +@Mixin(ItemStackRenderState.LayerRenderState.class) +public class UseOverlayableItemRenderer { + @ModifyExpressionValue(method = "submit", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/item/ItemStackRenderState$LayerRenderState;renderType:Lnet/minecraft/client/renderer/RenderType;", opcode = Opcodes.GETFIELD)) + private RenderType replace(RenderType original) { + if (EntityRenderTintEvent.overlayOverride != null && original instanceof RenderType.CompositeRenderType multiPhase && multiPhase.state.textureState instanceof RenderStateShard.TextureStateShard texture && texture.cutoutTexture().isPresent()) + return RenderType.entityTranslucent(texture.cutoutTexture().get()); + return original; + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableSkullBlockEntityRenderer.java b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableSkullBlockEntityRenderer.java new file mode 100644 index 0000000..ac102ed --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/entitytints/UseOverlayableSkullBlockEntityRenderer.java @@ -0,0 +1,25 @@ +package moe.nea.firmament.mixins.render.entitytints; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.events.EntityRenderTintEvent; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.SkullBlockRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +/** + * Patch to make {@link SkullBlockRenderer} use a {@link RenderType} that allows uses Minecraft's overlay texture, if a {@link EntityRenderTintEvent#overlayOverride} is specified. + */ + +@Mixin(SkullBlockRenderer.class) +public class UseOverlayableSkullBlockEntityRenderer { + @ModifyExpressionValue(method = "submitSkull(Lnet/minecraft/core/Direction;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/model/SkullModelBase;Lnet/minecraft/client/renderer/RenderType;ILnet/minecraft/client/renderer/feature/ModelFeatureRenderer$CrumblingOverlay;)V", + at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/OverlayTexture;NO_OVERLAY:I")) + private static int replaceUvIndex(int original) { + if (EntityRenderTintEvent.overlayOverride != null) + return OverlayTexture.pack(15, 10); // TODO: store this info in a global alongside overlayOverride + return original; + } + +} diff --git a/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java b/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java new file mode 100644 index 0000000..7c37684 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: LGPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 azureaaron via Skyblocker + */ + +package moe.nea.firmament.mixins.render.renderer; + +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import moe.nea.firmament.util.render.MultiSpecialGuiRenderState; +import moe.nea.firmament.util.render.MultiSpecialGuiRenderer; +import net.minecraft.client.gui.render.GuiRenderer; +import net.minecraft.client.gui.render.pip.PictureInPictureRenderer; +import net.minecraft.client.gui.render.state.GuiRenderState; +import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState; +import net.minecraft.client.renderer.MultiBufferSource; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.HashMap; +import java.util.Map; + +/** + * The structure of this class was roughly taken from SkyBlocker, retrieved 29.07.2025 + */ +@Mixin(GuiRenderer.class) +public class MultipleSpecialGuiRenderStates { + @Shadow + @Final + private MultiBufferSource.BufferSource bufferSource; + @Shadow + @Final + GuiRenderState renderState; + @Unique + Map<MultiSpecialGuiRenderState, MultiSpecialGuiRenderer<?>> multiRenderers = new HashMap<>(); + + @Inject(method = "preparePictureInPictureState", at = @At("HEAD"), cancellable = true) + private <T extends PictureInPictureRenderState> void onPrepareElement(T elementState, int windowScaleFactor, CallbackInfo ci) { + if (elementState instanceof MultiSpecialGuiRenderState multiState) { + @SuppressWarnings({"resource", "unchecked"}) + var renderer = (PictureInPictureRenderer<T>) multiRenderers + .computeIfAbsent(multiState, elementState$ -> elementState$.createRenderer(this.bufferSource)); + renderer.prepare(elementState, renderState, windowScaleFactor); + ci.cancel(); + } + } + + @Inject(method = "close", at = @At("TAIL")) + private void onClose(CallbackInfo ci) { + multiRenderers.values().forEach(PictureInPictureRenderer::close); + } + + @Inject(method = "render(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/render/GuiRenderer;clearUnusedOversizedItemRenderers()V")) + private void onAfterRender(GpuBufferSlice fogBuffer, CallbackInfo ci) { + multiRenderers.values().removeIf(it -> { + if (it.consumeRender()) { + return false; + } else { + it.close(); + return true; + } + }); + } +} diff --git a/src/main/java/moe/nea/firmament/repo/EnchantedBookCache.kt b/src/main/java/moe/nea/firmament/repo/EnchantedBookCache.kt new file mode 100644 index 0000000..0e276ce --- /dev/null +++ b/src/main/java/moe/nea/firmament/repo/EnchantedBookCache.kt @@ -0,0 +1,16 @@ +package moe.nea.firmament.repo + +import io.github.moulberry.repo.IReloadable +import io.github.moulberry.repo.NEURepository +import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.removeColorCodes +import moe.nea.firmament.util.skyblockId + +class EnchantedBookCache : IReloadable { + var byName: Map<String, SkyblockId> = mapOf() + override fun reload(repo: NEURepository) { + byName = repo.items.items.values + .filter { it.displayName.endsWith("Enchanted Book") } + .associate { it.lore.first().removeColorCodes() to it.skyblockId } + } +} diff --git a/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt b/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt new file mode 100644 index 0000000..29c7f15 --- /dev/null +++ b/src/main/java/moe/nea/firmament/util/data/ManagedConfig.kt @@ -0,0 +1,292 @@ +package moe.nea.firmament.util.data + +import com.mojang.serialization.Codec +import io.github.notenoughupdates.moulconfig.ChromaColour +import io.github.notenoughupdates.moulconfig.gui.CloseEventListener +import io.github.notenoughupdates.moulconfig.gui.GuiContext +import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent +import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent +import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent +import io.github.notenoughupdates.moulconfig.gui.component.RowComponent +import io.github.notenoughupdates.moulconfig.gui.component.ScrollPanelComponent +import io.github.notenoughupdates.moulconfig.gui.component.TextComponent +import io.github.notenoughupdates.moulconfig.platform.MoulConfigScreenComponent +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import moe.nea.firmament.Firmament +import moe.nea.firmament.gui.FirmButtonComponent +import moe.nea.firmament.gui.config.AllConfigsGui +import moe.nea.firmament.gui.config.BooleanHandler +import moe.nea.firmament.gui.config.ChoiceHandler +import moe.nea.firmament.gui.config.ClickHandler +import moe.nea.firmament.gui.config.ColourHandler +import moe.nea.firmament.gui.config.DurationHandler +import moe.nea.firmament.gui.config.GuiAppender +import moe.nea.firmament.gui.config.HudMeta +import moe.nea.firmament.gui.config.HudMetaHandler +import moe.nea.firmament.gui.config.HudPosition +import moe.nea.firmament.gui.config.IntegerHandler +import moe.nea.firmament.gui.config.KeyBindingHandler +import moe.nea.firmament.gui.config.ManagedOption +import moe.nea.firmament.gui.config.StringHandler +import moe.nea.firmament.keybindings.SavedKeyBinding +import moe.nea.firmament.util.ScreenUtil +import moe.nea.firmament.util.collections.InstanceList +import net.minecraft.client.gui.screens.Screen +import net.minecraft.network.chat.Component +import net.minecraft.util.StringRepresentable +import org.joml.Vector2i +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.putJsonObject +import kotlin.io.path.createDirectories +import kotlin.io.path.readText +import kotlin.io.path.writeText +import kotlin.time.Duration +import moe.nea.firmament.gui.config.storage.ConfigStorageClass + +abstract class ManagedConfig( + val name: String, + val category: Category, +) : IDataHolder<Unit>() { + enum class Category { + // Böse Kategorie, nicht benutzten lol + MISC, + CHAT, + INVENTORY, + ITEMS, + MINING, + GARDEN, + EVENTS, + INTEGRATIONS, + META, + DEV, + ; + + val labelText: Component = Component.translatable("firmament.config.category.${name.lowercase()}") + val description: Component = Component.translatable("firmament.config.category.${name.lowercase()}.description") + val configs: MutableList<ManagedConfig> = mutableListOf() + } + + companion object { + val allManagedConfigs = InstanceList<ManagedConfig>("ManagedConfig") + } + + interface OptionHandler<T : Any> { + fun initOption(opt: ManagedOption<T>) {} + fun toJson(element: T): JsonElement? + fun fromJson(element: JsonElement): T + fun emitGuiElements(opt: ManagedOption<T>, guiAppender: GuiAppender) + } + + init { + allManagedConfigs.getAll().forEach { + require(it.name != name) { "Duplicate name '$name' used for config" } + } + allManagedConfigs.add(this) + category.configs.add(this) + } + + override fun keys(): Collection<Unit> { + return listOf(Unit) + } + + override fun clear() { + sortedOptions.forEach { + it._actualValue = null + } + } + + override val storageClass: ConfigStorageClass + get() = ConfigStorageClass.CONFIG + + override fun saveTo(key: Unit): JsonObject { + return buildJsonObject { + putJsonObject(name) { + sortedOptions.forEach { + put(it.propertyName, it.toJson() ?: return@forEach) + } + } + } + } + + override fun explicitDefaultLoad() { + val empty = JsonObject(mapOf()) + sortedOptions.forEach { it.load(empty) } + } + + override fun loadFrom(key: Unit, jsonObject: JsonObject) { + val unprefixed = jsonObject[name]?.jsonObject ?: JsonObject(mapOf()) + sortedOptions.forEach { + it.load(unprefixed) + } + } + + val allOptions = mutableMapOf<String, ManagedOption<*>>() + val sortedOptions = mutableListOf<ManagedOption<*>>() + + private var latestGuiAppender: GuiAppender? = null + + protected fun <T : Any> option( + propertyName: String, + default: () -> T, + handler: OptionHandler<T> + ): ManagedOption<T> { + if (propertyName in allOptions) error("Cannot register the same name twice") + return ManagedOption(this, propertyName, default, handler).also { + it.handler.initOption(it) + allOptions[propertyName] = it + sortedOptions.add(it) + } + } + + protected fun toggle(propertyName: String, default: () -> Boolean): ManagedOption<Boolean> { + return option(propertyName, default, BooleanHandler(this)) + } + + protected fun colour(propertyName: String, default: () -> ChromaColour): ManagedOption<ChromaColour> { + return option(propertyName, default, ColourHandler(this)) + } + + protected fun <E> choice( + propertyName: String, + enumClass: Class<E>, + default: () -> E + ): ManagedOption<E> where E : Enum<E>, E : StringRepresentable { + return option(propertyName, default, ChoiceHandler(enumClass, enumClass.enumConstants.toList())) + } + + protected inline fun <reified E> choice( + propertyName: String, + noinline default: () -> E + ): ManagedOption<E> where E : Enum<E>, E : StringRepresentable { + return choice(propertyName, E::class.java, default) + } + + private fun <E> createStringIdentifiable(x: () -> Array<out E>): Codec<E> where E : Enum<E>, E : StringRepresentable { + return StringRepresentable.fromEnum { x() } + } + + // TODO: wait on https://youtrack.jetbrains.com/issue/KT-73434 +// protected inline fun <reified E> choice( +// propertyName: String, +// noinline default: () -> E +// ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable { +// return choice( +// propertyName, +// enumEntries<E>().toList(), +// StringIdentifiable.createCodec { enumValues<E>() }, +// EnumRenderer.default(), +// default +// ) +// } + open fun onChange(option: ManagedOption<*>) { + } + + protected fun duration( + propertyName: String, + min: Duration, + max: Duration, + default: () -> Duration, + ): ManagedOption<Duration> { + return option(propertyName, default, DurationHandler(this, min, max)) + } + + + protected fun position( + propertyName: String, + width: Int, + height: Int, + default: () -> Vector2i, + ): ManagedOption<HudMeta> { + val label = Component.translatable("firmament.config.${name}.${propertyName}") + return option(propertyName, { + val p = default() + HudMeta(HudPosition(p.x(), p.y(), 1F), Firmament.identifier(propertyName), label, width, height) + }, HudMetaHandler(this, propertyName, label, width, height)) + } + + protected fun keyBinding( + propertyName: String, + default: () -> Int, + ): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { + SavedKeyBinding.Companion.keyWithoutMods(default()) + } + + protected fun keyBindingWithOutDefaultModifiers( + propertyName: String, + default: () -> SavedKeyBinding, + ): ManagedOption<SavedKeyBinding> { + return option(propertyName, default, KeyBindingHandler("firmament.config.${name}.${propertyName}", this)) + } + + protected fun keyBindingWithDefaultUnbound( + propertyName: String, + ): ManagedOption<SavedKeyBinding> { + return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding.Companion.unbound() } + } + + protected fun integer( + propertyName: String, + min: Int, + max: Int, + default: () -> Int, + ): ManagedOption<Int> { + return option(propertyName, default, IntegerHandler(this, min, max)) + } + + protected fun button(propertyName: String, runnable: () -> Unit): ManagedOption<Unit> { + return option(propertyName, { }, ClickHandler(this, runnable)) + } + + protected fun string(propertyName: String, default: () -> String): ManagedOption<String> { + return option(propertyName, default, StringHandler(this)) + } + + + fun reloadGui() { + latestGuiAppender?.reloadables?.forEach { it() } + } + + val translationKey get() = "firmament.config.${name}" + val labelText: Component = Component.translatable(translationKey) + + fun getConfigEditor(parent: Screen? = null): Screen { + var screen: Screen? = null + val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } } + latestGuiAppender = guiapp + guiapp.appendFullRow( + RowComponent( + FirmButtonComponent(TextComponent("←")) { + if (parent != null) { + markDirty() + ScreenUtil.setScreenLater(parent) + } else { + AllConfigsGui.showAllGuis() + } + } + )) + sortedOptions.forEach { it.appendToGui(guiapp) } + guiapp.reloadables.forEach { it() } + val component = CenterComponent( + PanelComponent( + ScrollPanelComponent(400, 300, ColumnComponent(guiapp.panel)), + 10, + PanelComponent.DefaultBackgroundRenderer.VANILLA + ) + ) + screen = object : MoulConfigScreenComponent(Component.empty(), GuiContext(component), parent) { + override fun onClose() { + if (guiContext.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) { + minecraft!!.setScreen(parent) + } + } + } + return screen + } + + fun showConfigEditor(parent: Screen? = null) { + ScreenUtil.setScreenLater(getConfigEditor(parent)) + } + +} |
