Refactor mixins

This commit is contained in:
2026-03-08 13:34:45 +01:00
parent f182ac7e91
commit 8854726916
27 changed files with 193 additions and 170 deletions
+1 -1
Submodule common updated: da6576e4ed...56d9be1e5a
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.common.ref.game.ItemStackRef; import org.saturnclient.common.ref.game.ItemStackRef;
import org.saturnclient.feature.interfaces.ArmorDisplayInterface; import org.saturnclient.feature.features.featuresinterfaces.ArmorDisplayInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.EquipmentSlot;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.AutoSprintInterface; import org.saturnclient.feature.features.featuresinterfaces.AutoSprintInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.CoordinatesInterface; import org.saturnclient.feature.features.featuresinterfaces.CoordinatesInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.CrosshairInterface; import org.saturnclient.feature.features.featuresinterfaces.CrosshairInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.DayCounterInterface; import org.saturnclient.feature.features.featuresinterfaces.DayCounterInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.FpsInterface; import org.saturnclient.feature.features.featuresinterfaces.FpsInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.FreelookInterface; import org.saturnclient.feature.features.featuresinterfaces.FreelookInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.Perspective; import net.minecraft.client.option.Perspective;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.HealthDisplayInterface; import org.saturnclient.feature.features.featuresinterfaces.HealthDisplayInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.KeystrokesInterface; import org.saturnclient.feature.features.featuresinterfaces.KeystrokesInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.NametagsInterface; import org.saturnclient.feature.features.featuresinterfaces.NametagsInterface;
import org.saturnclient.modules.HealthRenderState; import org.saturnclient.modules.HealthRenderState;
import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.PingInterface; import org.saturnclient.feature.features.featuresinterfaces.PingInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
@@ -11,9 +11,11 @@ public class PingFabric implements PingInterface {
@Override @Override
public int getPing() { public int getPing() {
if (mc.player == null || mc.getNetworkHandler() == null) return 0; if (mc.player == null || mc.getNetworkHandler() == null)
return 0;
ClientPlayNetworkHandler handler = mc.getNetworkHandler(); ClientPlayNetworkHandler handler = mc.getNetworkHandler();
if (handler.getPlayerListEntry(mc.player.getUuid()) == null) return 0; if (handler.getPlayerListEntry(mc.player.getUuid()) == null)
return 0;
return handler.getPlayerListEntry(mc.player.getUuid()).getLatency(); return handler.getPlayerListEntry(mc.player.getUuid()).getLatency();
} }
} }
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules; package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.SpeedometerInterface; import org.saturnclient.feature.features.featuresinterfaces.SpeedometerInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -5,7 +5,7 @@ import java.util.stream.Collectors;
import java.util.Arrays; import java.util.Arrays;
import org.saturnclient.common.ref.asset.SpriteRef; import org.saturnclient.common.ref.asset.SpriteRef;
import org.saturnclient.feature.interfaces.StatusEffectsInterface; import org.saturnclient.feature.features.featuresinterfaces.StatusEffectsInterface;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.StatusEffectSpriteManager; import net.minecraft.client.texture.StatusEffectSpriteManager;
@@ -1,37 +0,0 @@
package org.saturnclient.impl.provider;
import org.saturnclient.feature.Feature;
import org.saturnclient.feature.FeatureManager.ModuleProvider;
import org.saturnclient.feature.features.*;
import org.saturnclient.impl.modules.*;
public class FabricModuleProvider implements ModuleProvider {
public static final Feature[] MODS = {
new ArmorDisplay(new ArmorDisplayFabric()),
new AutoSprint(new AutoSprintFabric()),
new Coordinates(new CoordinatesFabric()),
new Crosshair(new CrosshairFabric()),
new DayCounter(new DayCounterFabric()),
new Fps(new FpsFabric()),
new Freelook(new FreelookFabric()),
new HealthDisplay(new HealthDisplayFabric()),
new Keystrokes(new KeystrokesFabric()),
new Clock(),
new Fullbright(),
new Nametags(new NametagsFabric()),
new NoFog(),
new Ping(new PingFabric()),
new Speedometer(new SpeedometerFabric()),
new StatusEffects(new StatusEffectsFabric()),
new Tps(),
new Zoom()
};
@Override
public Feature[] getMods() {
return MODS;
}
}
@@ -1,6 +1,6 @@
package org.saturnclient.modules; package org.saturnclient.modules;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType; import org.saturnclient.common.module.EntityModule.EntityType;
public interface HealthRenderState { public interface HealthRenderState {
float saturn$getHealth(); float saturn$getHealth();
@@ -3,7 +3,7 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.BackgroundRenderer; import net.minecraft.client.render.BackgroundRenderer;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import org.saturnclient.feature.features.NoFog; import org.saturnclient.feature.features.NoFogFeature;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
@@ -14,19 +14,16 @@ public class BackgroundRendererMixin {
@ModifyVariable(method = "applyFog", at = @At("HEAD"), ordinal = 0, argsOnly = true) @ModifyVariable(method = "applyFog", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private static float modifyFogStart(float viewDistance) { private static float modifyFogStart(float viewDistance) {
if (NoFog.isActive()) { if (NoFogFeature.isActive()) {
return Float.MAX_VALUE; // this could be customised return Float.MAX_VALUE;
} }
return viewDistance; return viewDistance;
} }
// liquids
@Redirect( @Redirect(method = "applyFog", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSpectator()Z"))
method = "applyFog",
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSpectator()Z")
)
private static boolean redirectIsSpectator(Entity entity) { private static boolean redirectIsSpectator(Entity entity) {
if (NoFog.isActive() && NoFog.liquids()) { if (NoFogFeature.isActive() && NoFogFeature.liquids()) {
return true; // pretend to be spectator return true; // pretend to be spectator to bypass liquid fog
} }
return entity.isSpectator(); return entity.isSpectator();
} }
@@ -6,7 +6,7 @@ import net.minecraft.client.render.Camera;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.saturnclient.feature.features.Freelook; import org.saturnclient.feature.features.FreelookFeature;
import org.saturnclient.modules.CameraOverriddenEntity; import org.saturnclient.modules.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -17,28 +17,32 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class) @Mixin(Camera.class)
public abstract class CameraMixin { public abstract class CameraMixin {
@Unique @Unique
boolean firstTime = true; private boolean firstTime = true;
@Shadow @Shadow
protected abstract void setRotation(float yaw, float pitch); protected abstract void setRotation(float yaw, float pitch);
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER)) @Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER))
public void lockRotation(BlockView focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing, float tickDelta, CallbackInfo ci) { public void lockRotation(BlockView focusedBlock, Entity cameraEntity,
if (Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) { boolean isThirdPerson, boolean isFrontFacing,
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity; float tickDelta, CallbackInfo ci) {
if (FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
CameraOverriddenEntity overridden = (CameraOverriddenEntity) cameraEntity;
if (firstTime && MinecraftClient.getInstance().player != null) { if (firstTime && MinecraftClient.getInstance().player != null) {
cameraOverriddenEntity.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch()); overridden.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch());
cameraOverriddenEntity.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw()); overridden.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
firstTime = false; firstTime = false;
} }
this.setRotation(cameraOverriddenEntity.freelook$getCameraYaw(), cameraOverriddenEntity.freelook$getCameraPitch());
this.setRotation(overridden.freelook$getCameraYaw(), overridden.freelook$getCameraPitch());
} }
if (!Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
if (!FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
firstTime = true; firstTime = true;
} }
} }
} }
@@ -1,11 +1,10 @@
package org.saturnclient.modules.mixins; package org.saturnclient.modules.mixins;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.saturnclient.feature.features.Freelook; import org.saturnclient.feature.features.FreelookFeature;
import org.saturnclient.modules.CameraOverriddenEntity; import org.saturnclient.modules.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -15,48 +14,45 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class) @Mixin(Entity.class)
public class EntityMixin implements CameraOverriddenEntity { public class EntityMixin implements CameraOverriddenEntity {
@Unique
private float cameraPitch;
@Unique @Unique
private float cameraYaw; private float cameraPitch;
@Unique
@Inject(method = "changeLookDirection", at = @At("HEAD"), cancellable = true) private float cameraYaw;
public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) {
//noinspection ConstantValue// IntelliJ is incorrect here, this code block is reachable
if (Freelook.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
double pitchDelta = (yDelta * 0.15);
double yawDelta = (xDelta * 0.15);
this.cameraPitch = MathHelper.clamp(this.cameraPitch + (float) pitchDelta, -90.0f, 90.0f);
this.cameraYaw += (float) yawDelta;
@Inject(method = "changeLookDirection", at = @At("HEAD"), cancellable = true)
public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) {
// noinspection ConstantValue — reachable at runtime despite IDE warning
if (FreelookFeature.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
this.cameraPitch = MathHelper.clamp(
this.cameraPitch + (float) (yDelta * 0.15),
-90.0f, 90.0f);
this.cameraYaw += (float) (xDelta * 0.15);
ci.cancel(); ci.cancel();
} }
} }
@Override @Override
@Unique @Unique
public float freelook$getCameraPitch() { public float freelook$getCameraPitch() {
return this.cameraPitch; return cameraPitch;
} }
@Override @Override
@Unique @Unique
public float freelook$getCameraYaw() { public float freelook$getCameraYaw() {
return this.cameraYaw; return cameraYaw;
} }
@Override @Override
@Unique @Unique
public void freelook$setCameraPitch(float pitch) { public void freelook$setCameraPitch(float p) {
this.cameraPitch = pitch; cameraPitch = p;
} }
@Override @Override
@Unique @Unique
public void freelook$setCameraYaw(float yaw) { public void freelook$setCameraYaw(float y) {
this.cameraYaw = yaw; cameraYaw = y;
} }
} }
@@ -1,22 +1,20 @@
package org.saturnclient.modules.mixins; package org.saturnclient.modules.mixins;
import org.saturnclient.feature.features.Zoom; import net.minecraft.client.render.GameRenderer;
import org.saturnclient.feature.features.ZoomFeature;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.render.GameRenderer;
@Mixin(GameRenderer.class) @Mixin(GameRenderer.class)
public class GameRendererMixin { public class GameRendererMixin {
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true) @Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
private void onGetFov(CallbackInfoReturnable<Float> cir) { private void onGetFov(CallbackInfoReturnable<Float> cir) {
if (Zoom.isZooming && Zoom.shouldZoom()) { if (ZoomFeature.shouldZoom()) {
float originalFov = cir.getReturnValue(); cir.setReturnValue(cir.getReturnValue() / ZoomFeature.getZoomLevel());
float zoomedFov = originalFov / Zoom.getZoomLevel();
cir.setReturnValue(zoomedFov);
} }
} }
} }
@@ -4,20 +4,17 @@ package org.saturnclient.modules.mixins;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.render.LightmapTextureManager; import net.minecraft.client.render.LightmapTextureManager;
import org.saturnclient.feature.features.Fullbright; import org.saturnclient.feature.features.FullbrightFeature;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@Mixin(LightmapTextureManager.class) @Mixin(LightmapTextureManager.class)
public class LightmapTextureManagerMixin { public class LightmapTextureManagerMixin {
@ModifyExpressionValue( @ModifyExpressionValue(method = "update", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0))
method = "update",
at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)
)
private float modifyGamma(float original) { private float modifyGamma(float original) {
if (Fullbright.shouldOverrideBrightness()) { if (FullbrightFeature.shouldOverrideBrightness()) {
return Fullbright.getBrightnessValue(); return FullbrightFeature.getBrightnessValue();
} }
return original; return original;
} }
@@ -2,25 +2,33 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType; import org.saturnclient.common.module.EntityModule;
import org.saturnclient.modules.HealthRenderState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
/**
* Adds health / entity-type fields to {@link LivingEntityRenderState} so
* that {@link NametagsMixin} can read them without touching the live entity.
*
* The fields are populated each frame by {@link LivingEntityRendererMixin}.
*/
@Mixin(LivingEntityRenderState.class) @Mixin(LivingEntityRenderState.class)
public class LivingEntityRenderStateMixin implements org.saturnclient.modules.HealthRenderState { public class LivingEntityRenderStateMixin implements HealthRenderState {
@Unique private float saturn$health = 0f; @Unique private float saturn$health = 0f;
@Unique private float saturn$maxHealth = 1f; @Unique private float saturn$maxHealth = 1f;
@Unique private EntityType saturn$entityType = EntityType.OTHER; @Unique private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER;
public float saturn$getHealth() { return saturn$health; } @Override public float saturn$getHealth() { return saturn$health; }
public float saturn$getMaxHealth() { return saturn$maxHealth; } @Override public float saturn$getMaxHealth() { return saturn$maxHealth; }
@Override
public void saturn$setHealth(float health, float maxHealth) { public void saturn$setHealth(float health, float maxHealth) {
this.saturn$health = health; this.saturn$health = health;
this.saturn$maxHealth = maxHealth; this.saturn$maxHealth = maxHealth;
} }
public EntityType saturn$getEntityType() { return saturn$entityType; } @Override public EntityModule.EntityType saturn$getEntityType() { return saturn$entityType; }
public void saturn$setEntityType(EntityType type) { this.saturn$entityType = type; } @Override public void saturn$setEntityType(EntityModule.EntityType type) { saturn$entityType = type; }
} }
@@ -2,19 +2,22 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.entity.LivingEntityRenderer; import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType;
import org.saturnclient.modules.HealthRenderState;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.HostileEntity; import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.passive.PassiveEntity; import net.minecraft.entity.passive.PassiveEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import org.saturnclient.common.module.EntityModule;
import org.saturnclient.modules.HealthRenderState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Captures health and entity-type into the render state each frame so
* that the nametag replacement logic never needs to touch the live entity.
*/
@Mixin(LivingEntityRenderer.class) @Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> { public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> {
@@ -25,14 +28,16 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth()); hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth());
if (entity instanceof PlayerEntity) { EntityModule.EntityType type;
hrs.saturn$setEntityType(EntityType.PLAYER); if (entity instanceof PlayerEntity)
} else if (entity instanceof HostileEntity) { type = EntityModule.EntityType.PLAYER;
hrs.saturn$setEntityType(EntityType.HOSTILE); else if (entity instanceof HostileEntity)
} else if (entity instanceof PassiveEntity) { type = EntityModule.EntityType.HOSTILE;
hrs.saturn$setEntityType(EntityType.PASSIVE); else if (entity instanceof PassiveEntity)
} else { type = EntityModule.EntityType.PASSIVE;
hrs.saturn$setEntityType(EntityType.OTHER); else
} type = EntityModule.EntityType.OTHER;
hrs.saturn$setEntityType(type);
} }
} }
@@ -7,8 +7,9 @@ import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.saturnclient.feature.features.Nametags; import org.saturnclient.common.module.EntityModule;
import org.saturnclient.impl.modules.NametagsFabric; import org.saturnclient.feature.features.NametagsFeature;
import org.saturnclient.modules.HealthRenderState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -19,32 +20,85 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(EntityRenderer.class) @Mixin(EntityRenderer.class)
public abstract class NametagsMixin { public abstract class NametagsMixin {
/**
* Guards against infinite recursion: our inject calls
* {@code renderLabelIfPresent} again with the replacement text, so we
* use a thread-local flag to skip re-entry on that second call.
*/
@Unique @Unique
private static final ThreadLocal<Boolean> saturn$rendering = ThreadLocal.withInitial(() -> false); private static final ThreadLocal<Boolean> saturn$rendering = ThreadLocal.withInitial(() -> false);
@Shadow @Shadow
protected abstract <S extends EntityRenderState> void renderLabelIfPresent( protected abstract <S extends EntityRenderState> void renderLabelIfPresent(
S state, Text text, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light S state, Text text, MatrixStack matrices,
); VertexConsumerProvider vertexConsumers, int light);
@Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true) @Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true)
private <S extends EntityRenderState> void saturn$replaceNametag(S state, Text text, private <S extends EntityRenderState> void saturn$replaceNametag(
S state, Text text,
MatrixStack matrices, VertexConsumerProvider vertexConsumers, MatrixStack matrices, VertexConsumerProvider vertexConsumers,
int light, CallbackInfo ci) { int light, CallbackInfo ci) {
if (saturn$rendering.get()) return; if (saturn$rendering.get())
if (!Nametags.shouldReplaceName()) return; return;
if (!(state instanceof LivingEntityRenderState living)) return; if (!NametagsFeature.shouldReplaceName())
return;
if (!(state instanceof LivingEntityRenderState liv))
return;
String replacement = Nametags.getNametagString(new NametagsFabric.EntityStateImpl(living)); String replacement = NametagsFeature.getNametagString(
if (replacement == null) return; new RenderStateEntityState(liv));
if (replacement == null)
return;
ci.cancel(); ci.cancel();
saturn$rendering.set(true); saturn$rendering.set(true);
try { try {
renderLabelIfPresent(state, Text.literal(replacement), matrices, vertexConsumers, light); renderLabelIfPresent(state, Text.literal(replacement),
matrices, vertexConsumers, light);
} finally { } finally {
saturn$rendering.set(false); saturn$rendering.set(false);
} }
} }
// ------------------------------------------------------------------
// Inner adapter — bridges LivingEntityRenderState → EntityModule.EntityState
// Kept here so the mixin package is self-contained; no separate Fabric
// impl class is needed for this thin adapter.
// ------------------------------------------------------------------
@Unique
private static final class RenderStateEntityState implements EntityModule.EntityState {
private final LivingEntityRenderState state;
private final HealthRenderState hrs;
RenderStateEntityState(LivingEntityRenderState state) {
this.state = state;
// LivingEntityRenderStateMixin implements HealthRenderState
this.hrs = (state instanceof HealthRenderState h) ? h : null;
}
@Override
public String getCustomName() {
// The vanilla render state stores the display name as a Text;
// we expose it as a plain string for feature logic.
return state.customName != null ? state.customName.getString() : null;
}
@Override
public float getHealth() {
return hrs != null ? hrs.saturn$getHealth() : 0f;
}
@Override
public float getMaxHealth() {
return hrs != null ? hrs.saturn$getMaxHealth() : 1f;
}
@Override
public EntityModule.EntityType getEntityType() {
return hrs != null ? hrs.saturn$getEntityType() : EntityModule.EntityType.OTHER;
}
}
} }
@@ -1,8 +1,8 @@
package org.saturnclient.modules.mixins; package org.saturnclient.modules.mixins;
import org.saturnclient.feature.HudFeature;
import org.saturnclient.feature.FeatureLayout; import org.saturnclient.feature.FeatureLayout;
import org.saturnclient.feature.FeatureManager; import org.saturnclient.feature.FeatureManager;
import org.saturnclient.feature.HudFeature;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.RenderScope;
@@ -3,7 +3,7 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket;
import org.saturnclient.feature.features.Tps; import org.saturnclient.feature.features.TpsFeature;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -14,6 +14,6 @@ public class TpsMixin {
@Inject(method = "onWorldTimeUpdate", at = @At("HEAD")) @Inject(method = "onWorldTimeUpdate", at = @At("HEAD"))
private void onWorldTimeUpdate(WorldTimeUpdateS2CPacket packet, CallbackInfo ci) { private void onWorldTimeUpdate(WorldTimeUpdateS2CPacket packet, CallbackInfo ci) {
Tps.onTimePacket(packet.time()); TpsFeature.onTimePacket(packet.time());
} }
} }
@@ -11,7 +11,6 @@ import org.saturnclient.cosmetics.Hats;
import org.saturnclient.feature.FeatureManager; import org.saturnclient.feature.FeatureManager;
import org.saturnclient.cosmetics.Cloaks; import org.saturnclient.cosmetics.Cloaks;
import org.saturnclient.common.provider.Providers; import org.saturnclient.common.provider.Providers;
import org.saturnclient.impl.provider.FabricModuleProvider;
import org.saturnclient.impl.provider.GLFWProviderImpl; import org.saturnclient.impl.provider.GLFWProviderImpl;
import org.saturnclient.impl.provider.RefConstructorImpl; import org.saturnclient.impl.provider.RefConstructorImpl;
import org.saturnclient.impl.provider.SaturnProviderImpl; import org.saturnclient.impl.provider.SaturnProviderImpl;
@@ -44,7 +43,7 @@ public class SaturnClient implements ModInitializer {
SkinPreview.DRAWER = new EntityDrawerImpl(); SkinPreview.DRAWER = new EntityDrawerImpl();
Config.init(); Config.init();
FeatureManager.init(new FabricModuleProvider()); FeatureManager.init();
client.execute(() -> { client.execute(() -> {
SaturnScreenFabric.preload(client); SaturnScreenFabric.preload(client);