Update mod version and add Mixin support

This commit is contained in:
2026-01-06 20:20:55 +01:00
parent 0d87401849
commit f8df852136
9 changed files with 158 additions and 59 deletions
+29 -18
View File
@@ -1,8 +1,10 @@
plugins {
id 'java'
id 'eclipse'
id 'idea'
id 'maven-publish'
id 'net.minecraftforge.gradle' version '[6.0,6.2)'
id 'org.spongepowered.mixin' version '0.7.+'
}
version = mod_version
@@ -26,11 +28,8 @@ minecraft {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
// IMPORTANT: allow Mixin remapping
property 'mixin.env.remapRefMap', 'true'
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"
property "mixin.env.remapRefMap", "true"
property "mixin.env.refMapRemappingFile", file("$buildDir/createSrgToMcp/output.srg").absolutePath
mods {
"${mod_id}" {
source sourceSets.main
@@ -40,6 +39,9 @@ minecraft {
client {
property 'forge.enabledGameTestNamespaces', mod_id
property "mixin.debug.verbose", "true"
property "mixin.debug.export", "true"
property "mixin.dumpTargetOnFailure", "true"
}
server {
property 'forge.enabledGameTestNamespaces', mod_id
@@ -87,11 +89,11 @@ repositories {
dependencies {
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
compileOnly fg.deobf("net.fretux.ascend:Ascend:2.1.1")
implementation fg.deobf("net.fretux.ascend:Ascend:2.1.1")
runtimeOnly fg.deobf("net.fretux.ascend:Ascend:2.1.1")
// Iron's Spellbooks
compileOnly fg.deobf("io.redspace.ironsspellbooks:irons_spellbooks:${irons_spells_version}:api")
implementation fg.deobf("io.redspace.ironsspellbooks:irons_spellbooks:${irons_spells_version}")
runtimeOnly fg.deobf("io.redspace.ironsspellbooks:irons_spellbooks:${irons_spells_version}")
// GECKOLIB
implementation fg.deobf("software.bernie.geckolib:geckolib-forge-${geckolib_version}")
@@ -106,22 +108,31 @@ dependencies {
// Curios
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}:api")
implementation fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}")
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
}
mixin {
add sourceSets.main, "mixins.mindmotion.refmap.json"
debug.verbose = true
debug.export = true
dumpTargetOnFailure = true
}
tasks.named('processResources', ProcessResources).configure {
var replaceProperties = [
minecraft_version : minecraft_version,
minecraft_version_range : minecraft_version_range,
forge_version : forge_version,
forge_version_range : forge_version_range,
loader_version_range : loader_version_range,
mod_id : mod_id,
mod_name : mod_name,
mod_license : mod_license,
mod_version : mod_version,
mod_authors : mod_authors,
mod_description : mod_description,
minecraft_version : minecraft_version,
minecraft_version_range: minecraft_version_range,
forge_version : forge_version,
forge_version_range : forge_version_range,
loader_version_range : loader_version_range,
mod_id : mod_id,
mod_name : mod_name,
mod_license : mod_license,
mod_version : mod_version,
mod_authors : mod_authors,
mod_description : mod_description,
]
inputs.properties replaceProperties
+1 -1
View File
@@ -52,7 +52,7 @@ mod_name=Mind & Motion
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=All Rights Reserved
# The mod version. See https://semver.org/
mod_version=1.2.2
mod_version=1.2.3
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
+1 -1
View File
@@ -1,2 +1,2 @@
#This file is generated by updateDaemonJvm
toolchainVersion=22
toolchainVersion=17
@@ -1,6 +1,5 @@
package net.fretux.mindmotion.client.effects;
import io.redspace.ironsspellbooks.api.registry.AttributeRegistry;
import net.fretux.mindmotion.ConfigMM;
import net.fretux.mindmotion.player.PlayerCapabilityProvider;
import net.minecraft.world.entity.ai.attributes.Attribute;
@@ -9,38 +8,65 @@ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.fml.ModList;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;
public final class IronsSpellbooksCompatMM {
private static final UUID TEMPO_MANA_REGEN_UUID =
UUID.fromString("de4f16ad-73b3-4b99-bf77-44c9878222aa");
private static final String IRONS_MODID = "irons_spellbooks";
private static final String ATTR_REGISTRY_CLASS = "io.redspace.ironsspellbooks.api.registry.AttributeRegistry";
private static final String MANA_REGEN_FIELD = "MANA_REGEN";
private static boolean resolved = false;
private static boolean available = false;
private static Object manaRegenHolder = null;
private static Method holderGetMethod = null;
private IronsSpellbooksCompatMM() {
}
public static int getTempoPerPercent() {
return ConfigMM.COMMON.TEMPO_PER_MANA_REGEN_PERCENT.get();
}
private static final UUID TEMPO_MANA_REGEN_UUID =
UUID.fromString("de4f16ad-73b3-4b99-bf77-44c9878222aa");
private IronsSpellbooksCompatMM() {}
public static boolean isLoaded() {
return ModList.get().isLoaded("irons_spellbooks");
return ModList.get().isLoaded(IRONS_MODID);
}
public static void applyTempo(Player player) {
if (!isLoaded()) return;
resolveOnce();
if (!available) return;
player.getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(tempoCap -> {
int tempo = tempoCap.getTempo();
applyTempoToManaRegen(player, tempo);
});
}
private static void resolveOnce() {
if (resolved) return;
resolved = true;
try {
Class<?> attrReg = Class.forName(ATTR_REGISTRY_CLASS);
Field manaRegenField = attrReg.getField(MANA_REGEN_FIELD);
manaRegenHolder = manaRegenField.get(null);
if (manaRegenHolder == null) return;
holderGetMethod = manaRegenHolder.getClass().getMethod("get");
available = true;
} catch (Throwable t) {
available = false;
}
}
public static void applyTempoToManaRegen(Player player, int tempo) {
Attribute manaRegenAttr = (Attribute) AttributeRegistry.MANA_REGEN.get();
if (!available) return;
Attribute manaRegenAttr = getManaRegenAttribute();
if (manaRegenAttr == null) return;
AttributeInstance inst = player.getAttribute(manaRegenAttr);
if (inst == null) return;
inst.removeModifier(TEMPO_MANA_REGEN_UUID);
if (tempo <= 0) {
return;
}
if (tempo <= 0) return;
double bonusPercent = (double) tempo / getTempoPerPercent();
double multiplier = bonusPercent / 100.0;
inst.addTransientModifier(new AttributeModifier(
@@ -50,4 +76,13 @@ public final class IronsSpellbooksCompatMM {
AttributeModifier.Operation.MULTIPLY_BASE
));
}
private static Attribute getManaRegenAttribute() {
try {
Object attrObj = holderGetMethod.invoke(manaRegenHolder);
return (attrObj instanceof Attribute a) ? a : null;
} catch (Throwable t) {
return null;
}
}
}
@@ -1,8 +1,10 @@
package net.fretux.mindmotion.client.effects;
import com.mojang.logging.LogUtils;
import net.fretux.mindmotion.ConfigMM;
import net.fretux.mindmotion.client.ClientData;
import net.fretux.mindmotion.client.shader.VentShaderHandler;
import net.fretux.mindmotion.mixin.PostChainAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.PostChain;
import net.minecraft.client.renderer.PostPass;
@@ -13,29 +15,34 @@ import net.minecraftforge.client.event.RenderLevelStageEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import org.slf4j.Logger;
import java.lang.reflect.Field;
import java.util.List;
@Mod.EventBusSubscriber(modid = "mindmotion", value = Dist.CLIENT)
@Mod.EventBusSubscriber(modid = "mindmotion", value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class SanityShaderHandler {
private static final ResourceLocation DESAT_EFFECT =
new ResourceLocation("mindmotion", "shaders/post/desaturate.json");
ResourceLocation.fromNamespaceAndPath("mindmotion", "shaders/post/desaturate.json");
private static float currentStrength = 0f;
private static Field passesField;
private static final Logger LOGGER = LogUtils.getLogger();
private static final Field PASSES_FIELD =
ObfuscationReflectionHelper.findField(PostChain.class, "f_110009_");
private static List<PostPass> getPasses(PostChain chain) throws IllegalAccessException, NoSuchFieldException {
if (passesField == null) {
passesField = PostChain.class.getDeclaredField("passes");
passesField.setAccessible(true);
@SuppressWarnings("unchecked")
private static List<PostPass> safeGetPasses(PostChain chain) {
try {
return (List<PostPass>) PASSES_FIELD.get(chain);
} catch (IllegalAccessException e) {
LOGGER.error("Failed to access PostChain.passes", e);
return List.of();
}
@SuppressWarnings("unchecked")
List<PostPass> passes = (List<PostPass>) passesField.get(chain);
return passes;
}
@SubscribeEvent
public static void onRenderWorld(RenderLevelStageEvent event) {
Minecraft mc = Minecraft.getInstance();
@@ -48,6 +55,7 @@ public class SanityShaderHandler {
float desatMultiplier = ConfigMM.CLIENT.SANITY_DESAT_MULTIPLIER.get().floatValue();
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_LEVEL) return;
if (mc.player == null || mc.level == null) return;
LOGGER.debug("SanityShaderHandler tick stage={}", event.getStage());
float sanity = ClientData.SANITY;
float insanity = ClientData.INSANITY;
float maxSanity = ClientData.MAX_SANITY;
@@ -71,15 +79,34 @@ public class SanityShaderHandler {
if (mc.gameRenderer.currentEffect() == null) {
try {
mc.gameRenderer.loadEffect(DESAT_EFFECT);
PostChain chain = mc.gameRenderer.currentEffect();
LOGGER.info("Loaded post chain = {}", chain != null);
if (chain != null) {
try {
for (PostPass pass : safeGetPasses(chain)) {
EffectInstance fx = pass.getEffect();
LOGGER.info("pass fx={}", fx != null ? fx.getName() : "null");
if (fx != null) {
LOGGER.info(" Desat uniform={}", fx.getUniform("Desat") != null);
}
}
} catch (Throwable t) {
LOGGER.error("Failed to read PostChain passes via reflection", t);
}
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("Failed to read PostChain passes via reflection", e);
return;
}
}
PostChain chain = mc.gameRenderer.currentEffect();
LOGGER.info("effect class={}, accessorApplied={}",
chain == null ? "null" : chain.getClass().getName(),
chain instanceof PostChainAccessor
);
if (chain == null) return;
try {
for (PostPass pass : getPasses(chain)) {
for (PostPass pass : safeGetPasses(chain)) {
EffectInstance effect = pass.getEffect();
if (effect == null) continue;
var uDesat = effect.getUniform("Desat");
@@ -144,7 +171,7 @@ public class SanityShaderHandler {
}
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.info("Error loading post chain{}", e.getMessage());
}
}
@@ -0,0 +1,14 @@
package net.fretux.mindmotion.mixin;
import net.minecraft.client.renderer.PostChain;
import net.minecraft.client.renderer.PostPass;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(PostChain.class)
public interface PostChainAccessor {
@Accessor("passes")
List<PostPass> mindmotion$getPasses();
}
+1 -1
View File
@@ -40,7 +40,7 @@ authors="${mod_authors}" #optional
# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value.
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself.
#displayTest="MATCH_VERSION" # if nothing is specified, MATCH_VERSION is the default when clientSideOnly=false, otherwise IGNORE_ALL_VERSION when clientSideOnly=true (#optional)
mixinConfigs="mixins.mindmotion.json"
# The description text for the mod (multi line!) (#mandatory)
description='''${mod_description}'''
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
@@ -6,23 +6,25 @@ uniform mat4 ProjMat;
uniform vec2 OutSize;
uniform float ShakeStrength;
uniform float Time;
out vec2 oneTexel;
out vec2 oneTexel;
out vec2 texCoord;
void main() {
oneTexel = 1.0 / OutSize;
// screen-shake offset in *pixels*
float px = sin(Time * 25.0 + Position.x * 5.0) * ShakeStrength * 20.0;
float py = cos(Time * 20.0 + Position.y * 7.0) * ShakeStrength * 20.0;
// convert from pixels → normalized coordinates
vec2 offset = vec2(px / OutSize.x, py / OutSize.y);
vec2 shaken = Position.xy + offset;
texCoord = shaken / OutSize;
vec4 outPos = ProjMat * vec4(shaken, 0.0, 1.0);
vec2 uv = Position.xy;
vec2 posPx = Position.xy;
if (Position.x > 2.0 || Position.y > 2.0) {
uv = Position.xy / OutSize;
posPx = Position.xy;
} else {
uv = Position.xy;
posPx = Position.xy * OutSize;
}
float px = sin(Time * 25.0 + posPx.x * 0.01) * ShakeStrength * 20.0;
float py = cos(Time * 20.0 + posPx.y * 0.01) * ShakeStrength * 20.0;
vec2 uvOffset = vec2(px / OutSize.x, py / OutSize.y);
texCoord = uv + uvOffset;
vec4 outPos = ProjMat * vec4(posPx, 0.0, 1.0);
gl_Position = vec4(outPos.xy, 0.0, 1.0);
}
+10
View File
@@ -0,0 +1,10 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fretux.mindmotion.mixin",
"refmap": "mixins.mindmotion.refmap.json",
"compatibilityLevel": "JAVA_17",
"client": [
"PostChainAccessor"
]
}