Mixins
This commit is contained in:
@@ -7,7 +7,7 @@ import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.modules.HealthRenderState;
|
||||
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link EntityModule}.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
|
||||
/**
|
||||
* Injected interface on {@link net.minecraft.client.render.entity.state.LivingEntityRenderState}.
|
||||
*
|
||||
* Carries the entity's health snapshot and classification into the render
|
||||
* pipeline so that {@link NametagsMixin} can read them without touching
|
||||
* the live entity again.
|
||||
*/
|
||||
public interface HealthRenderState {
|
||||
|
||||
float saturn$getHealth();
|
||||
float saturn$getMaxHealth();
|
||||
void saturn$setHealth(float health, float maxHealth);
|
||||
|
||||
EntityModule.EntityType saturn$getEntityType();
|
||||
void saturn$setEntityType(EntityModule.EntityType type);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
|
||||
import org.saturnclient.feature.features.ZoomFeature;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Overrides the game's field-of-view when {@link ZoomFeature} is active.
|
||||
*
|
||||
* The old code checked both {@code Zoom.isZooming} and
|
||||
* {@code Zoom.shouldZoom()} separately. The refactored
|
||||
* {@link ZoomFeature#shouldZoom()} already ORs both conditions
|
||||
* ({@code enabled.value && isZooming}), so only one check is needed.
|
||||
*
|
||||
* No {@link org.saturnclient.common.module.ModuleProvider} is required
|
||||
* here because zooming is a pure render-time override with no per-tick
|
||||
* engine queries.
|
||||
*/
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
|
||||
/**
|
||||
* After the vanilla FOV has been computed, divide it by the zoom
|
||||
* level to produce the narrowed field-of-view.
|
||||
*/
|
||||
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
|
||||
private void onGetFov(CallbackInfoReturnable<Float> cir) {
|
||||
if (ZoomFeature.shouldZoom()) {
|
||||
float zoomedFov = cir.getReturnValue() / ZoomFeature.getZoomLevel();
|
||||
cir.setReturnValue(zoomedFov);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -1,5 +1,6 @@
|
||||
// code modified from Gamma utils: https://github.com/Sjouwer/gamma-utils
|
||||
package org.saturnclient.modules.mixins;
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
// Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
@@ -8,9 +9,22 @@ import org.saturnclient.feature.features.FullbrightFeature;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Overrides the lightmap's gamma computation when
|
||||
* {@link FullbrightFeature} is active, replacing the vanilla
|
||||
* brightness with a user-configured value.
|
||||
*
|
||||
* The old import was {@code org.saturnclient.feature.features.Fullbright};
|
||||
* it is now {@link FullbrightFeature}. The injection target and
|
||||
* strategy are unchanged.
|
||||
*/
|
||||
@Mixin(LightmapTextureManager.class)
|
||||
public class LightmapTextureManagerMixin {
|
||||
|
||||
/**
|
||||
* If fullbright is active, substitute the feature's brightness
|
||||
* value for whatever vanilla calculated.
|
||||
*/
|
||||
@ModifyExpressionValue(method = "update", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0))
|
||||
private float modifyGamma(float original) {
|
||||
if (FullbrightFeature.shouldOverrideBrightness()) {
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.impl.modules.HealthRenderState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
/**
|
||||
* Implements {@link HealthRenderState} on
|
||||
* {@link LivingEntityRenderState}, injecting health and entity-type
|
||||
* storage into every living entity's render-state snapshot.
|
||||
*
|
||||
* Previously imported {@code NametagsInterface.EntityType}; the enum
|
||||
* now lives in {@link EntityModule.EntityType} which is the single
|
||||
* canonical definition shared by the mixin layer and the feature layer.
|
||||
*
|
||||
* The stored values are written by
|
||||
* {@link LivingEntityRendererMixin#saturn$captureHealth} each time a
|
||||
* render state is extracted from a live entity, and read by
|
||||
* {@link NametagsMixin} at render time.
|
||||
*/
|
||||
@Mixin(LivingEntityRenderState.class)
|
||||
public class LivingEntityRenderStateMixin implements HealthRenderState {
|
||||
|
||||
@Unique
|
||||
private float saturn$health = 0f;
|
||||
@Unique
|
||||
private float saturn$maxHealth = 1f;
|
||||
@Unique
|
||||
private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER;
|
||||
|
||||
@Override
|
||||
public float saturn$getHealth() {
|
||||
return saturn$health;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float saturn$getMaxHealth() {
|
||||
return saturn$maxHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saturn$setHealth(float health, float maxHealth) {
|
||||
this.saturn$health = health;
|
||||
this.saturn$maxHealth = maxHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityModule.EntityType saturn$getEntityType() {
|
||||
return saturn$entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saturn$setEntityType(EntityModule.EntityType type) {
|
||||
saturn$entityType = type;
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -1,4 +1,4 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
@@ -8,15 +8,22 @@ import net.minecraft.entity.passive.PassiveEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.modules.HealthRenderState;
|
||||
import org.saturnclient.impl.modules.HealthRenderState;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Captures health and entity-type into the render state each frame so
|
||||
* that the nametag replacement logic never needs to touch the live entity.
|
||||
* Captures each living entity's health and type into the render state
|
||||
* during {@code updateRenderState}, so that nametag rendering can read
|
||||
* them without touching the live entity again.
|
||||
*
|
||||
* Previously imported {@code NametagsInterface.EntityType}; now uses
|
||||
* {@link EntityModule.EntityType} — the canonical enum shared across
|
||||
* the whole module system.
|
||||
*
|
||||
* The injection target and strategy are unchanged from the original.
|
||||
*/
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> {
|
||||
@@ -28,6 +35,7 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
|
||||
|
||||
hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth());
|
||||
|
||||
// Classify entity type for the nametag feature's filtering logic
|
||||
EntityModule.EntityType type;
|
||||
if (entity instanceof PlayerEntity)
|
||||
type = EntityModule.EntityType.PLAYER;
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import org.saturnclient.feature.features.NametagsFeature;
|
||||
import org.saturnclient.impl.modules.EntityModuleFabric;
|
||||
import org.saturnclient.impl.modules.HealthRenderState;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Intercepts nametag rendering and replaces the displayed text with
|
||||
* the string produced by {@link NametagsFeature#getNametagString}.
|
||||
*
|
||||
* Key changes from the original:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is
|
||||
* replaced by {@link EntityModuleFabric.RenderStateEntityState},
|
||||
* which wraps the {@link HealthRenderState} already injected into
|
||||
* the render state by {@link LivingEntityRenderStateMixin}.</li>
|
||||
* <li>The feature import changes from {@code Nametags} to
|
||||
* {@link NametagsFeature}; the two static methods
|
||||
* ({@code shouldReplaceName} / {@code getNametagString}) are
|
||||
* unchanged in signature.</li>
|
||||
* <li>The entity's display name is extracted from the render state's
|
||||
* {@code nameVisible} / text fields where available, falling back
|
||||
* to the plain text string passed to this method.</li>
|
||||
* </ul>
|
||||
*
|
||||
* The re-entrancy guard ({@code saturn$rendering}) is kept intact to
|
||||
* prevent infinite recursion when we call {@code renderLabelIfPresent}
|
||||
* with the replacement text.
|
||||
*/
|
||||
@Mixin(EntityRenderer.class)
|
||||
public abstract class NametagsMixin {
|
||||
|
||||
/** Guards against the recursive call we make with the replacement text. */
|
||||
@Unique
|
||||
private static final ThreadLocal<Boolean> saturn$rendering = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
@Shadow
|
||||
protected abstract <S extends EntityRenderState> void renderLabelIfPresent(
|
||||
S state, Text text,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light);
|
||||
|
||||
@Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true)
|
||||
private <S extends EntityRenderState> void saturn$replaceNametag(
|
||||
S state, Text text,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers,
|
||||
int light, CallbackInfo ci) {
|
||||
|
||||
// Skip our own recursive call
|
||||
if (saturn$rendering.get())
|
||||
return;
|
||||
|
||||
if (!NametagsFeature.shouldReplaceName())
|
||||
return;
|
||||
|
||||
// Only living entities carry health data
|
||||
if (!(state instanceof LivingEntityRenderState living))
|
||||
return;
|
||||
if (!(living instanceof HealthRenderState hrs))
|
||||
return;
|
||||
|
||||
// Build the platform-neutral EntityState from the render-state snapshot
|
||||
// (health/type were written by LivingEntityRendererMixin at extract time).
|
||||
String customName = text != null ? text.getString() : null;
|
||||
EntityModuleFabric.RenderStateEntityState entityState = new EntityModuleFabric.RenderStateEntityState(
|
||||
customName, hrs);
|
||||
|
||||
// Ask the feature for the replacement string
|
||||
String replacement = NametagsFeature.getNametagString(entityState);
|
||||
if (replacement == null)
|
||||
return; // feature decided not to replace this entity
|
||||
|
||||
ci.cancel();
|
||||
saturn$rendering.set(true);
|
||||
try {
|
||||
renderLabelIfPresent(state, Text.literal(replacement), matrices, vertexConsumers, light);
|
||||
} finally {
|
||||
saturn$rendering.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.hud.InGameHud;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
|
||||
import org.saturnclient.feature.Feature;
|
||||
import org.saturnclient.feature.FeatureLayout;
|
||||
import org.saturnclient.feature.FeatureManager;
|
||||
import org.saturnclient.feature.HudFeature;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.RenderScopeImpl;
|
||||
import org.saturnclient.ui.SaturnScreenFabric;
|
||||
import org.saturnclient.ui.screens.HudEditor;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Drives per-frame rendering for all registered features.
|
||||
*
|
||||
* This mixin is intentionally kept thin: it creates a {@link RenderScope},
|
||||
* iterates the enabled feature list from {@link FeatureManager}, and
|
||||
* dispatches either {@link HudFeature#renderHud} (for HUD elements) or
|
||||
* {@link Feature#render} (for world-overlay features like Crosshair).
|
||||
*
|
||||
* No feature-specific logic lives here — all decisions about what to
|
||||
* draw are made inside each feature class.
|
||||
*
|
||||
* Changes from the original:
|
||||
* - Package imports updated to the refactored feature names (no
|
||||
* more {@code feature.features.*}).
|
||||
* - The HUD-editor screen check still uses the existing
|
||||
* {@link SaturnScreenFabric} / {@link HudEditor} pair; that is
|
||||
* unchanged infrastructure.
|
||||
*/
|
||||
@Mixin(InGameHud.class)
|
||||
public class RenderMixin {
|
||||
|
||||
@Inject(method = "renderChat", at = @At("TAIL"))
|
||||
public void renderMods(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||
TextRenderer textRenderer = SaturnClient.client.textRenderer;
|
||||
|
||||
// Skip rendering while the HUD editor is open so the editor
|
||||
// can draw its own preview layout without double-drawing.
|
||||
if (textRenderer == null)
|
||||
return;
|
||||
if (SaturnClient.client.currentScreen instanceof SaturnScreenFabric f
|
||||
&& f.screen instanceof HudEditor)
|
||||
return;
|
||||
|
||||
RenderScope scope = new RenderScopeImpl(
|
||||
context.getMatrices(),
|
||||
((DrawContextAccessor) context).getVertexConsumers());
|
||||
|
||||
for (Feature feature : FeatureManager.ENABLED_MODS) {
|
||||
if (!feature.isEnabled())
|
||||
continue;
|
||||
|
||||
if (feature instanceof HudFeature hud) {
|
||||
FeatureLayout dim = hud.getDimensions();
|
||||
|
||||
scope.getMatrixStack().push();
|
||||
scope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0f);
|
||||
scope.getMatrixStack().scale(dim.scale.value, dim.scale.value, 1.0f);
|
||||
|
||||
if (dim.renderBackground) {
|
||||
scope.drawRoundedRectangle(0, 0, dim.width, dim.height,
|
||||
dim.radius.value, dim.bgColor.value);
|
||||
}
|
||||
|
||||
hud.renderHud(scope);
|
||||
scope.getMatrixStack().pop();
|
||||
|
||||
} else {
|
||||
// Non-HUD features (e.g. CrosshairFeature) use the plain render hook.
|
||||
feature.render(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-1
@@ -1,4 +1,4 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
package org.saturnclient.impl.modules.mixins;
|
||||
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket;
|
||||
@@ -9,6 +9,17 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Feeds world-time packets into {@link TpsFeature}'s TPS estimator.
|
||||
*
|
||||
* This is the only place in the entire codebase that reads a network
|
||||
* packet and passes data to a feature. The feature's
|
||||
* {@link TpsFeature#onTimePacket(long)} method handles all averaging
|
||||
* logic; the mixin is intentionally reduced to a single forwarding call.
|
||||
*
|
||||
* The only change from the original is renaming the import from
|
||||
* {@code feature.features.Tps} to {@link TpsFeature}.
|
||||
*/
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public class TpsMixin {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.saturnclient.modules;
|
||||
|
||||
public interface CameraOverriddenEntity {
|
||||
float freelook$getCameraPitch();
|
||||
float freelook$getCameraYaw();
|
||||
|
||||
void freelook$setCameraPitch(float pitch);
|
||||
void freelook$setCameraYaw(float yaw);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.saturnclient.modules;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
|
||||
/**
|
||||
* Mixed into {@link net.minecraft.client.render.entity.state.LivingEntityRenderState}
|
||||
* by {@link org.saturnclient.modules.mixins.LivingEntityRenderStateMixin}.
|
||||
*
|
||||
* Carries the health snapshot and entity classification that
|
||||
* {@link org.saturnclient.modules.mixins.NametagsMixin} needs without
|
||||
* accessing the live entity during rendering.
|
||||
*/
|
||||
public interface HealthRenderState {
|
||||
|
||||
float saturn$getHealth();
|
||||
float saturn$getMaxHealth();
|
||||
|
||||
void saturn$setHealth(float health, float maxHealth);
|
||||
|
||||
EntityModule.EntityType saturn$getEntityType();
|
||||
void saturn$setEntityType(EntityModule.EntityType type);
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.BackgroundRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
import org.saturnclient.feature.features.NoFogFeature;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(BackgroundRenderer.class)
|
||||
public class BackgroundRendererMixin {
|
||||
|
||||
@ModifyVariable(method = "applyFog", at = @At("HEAD"), ordinal = 0, argsOnly = true)
|
||||
private static float modifyFogStart(float viewDistance) {
|
||||
if (NoFogFeature.isActive()) {
|
||||
return Float.MAX_VALUE;
|
||||
}
|
||||
return viewDistance;
|
||||
}
|
||||
|
||||
@Redirect(method = "applyFog", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSpectator()Z"))
|
||||
private static boolean redirectIsSpectator(Entity entity) {
|
||||
if (NoFogFeature.isActive() && NoFogFeature.liquids()) {
|
||||
return true; // pretend to be spectator to bypass liquid fog
|
||||
}
|
||||
return entity.isSpectator();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.BlockView;
|
||||
|
||||
import org.saturnclient.feature.features.FreelookFeature;
|
||||
import org.saturnclient.modules.CameraOverriddenEntity;
|
||||
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;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public abstract class CameraMixin {
|
||||
|
||||
@Unique
|
||||
private boolean firstTime = true;
|
||||
|
||||
@Shadow
|
||||
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))
|
||||
public void lockRotation(BlockView focusedBlock, Entity cameraEntity,
|
||||
boolean isThirdPerson, boolean isFrontFacing,
|
||||
float tickDelta, CallbackInfo ci) {
|
||||
|
||||
if (FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
|
||||
CameraOverriddenEntity overridden = (CameraOverriddenEntity) cameraEntity;
|
||||
|
||||
if (firstTime && MinecraftClient.getInstance().player != null) {
|
||||
overridden.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch());
|
||||
overridden.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
this.setRotation(overridden.freelook$getCameraYaw(), overridden.freelook$getCameraPitch());
|
||||
}
|
||||
|
||||
if (!FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
|
||||
firstTime = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import org.saturnclient.feature.features.FreelookFeature;
|
||||
import org.saturnclient.modules.CameraOverriddenEntity;
|
||||
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(Entity.class)
|
||||
public class EntityMixin implements CameraOverriddenEntity {
|
||||
|
||||
@Unique
|
||||
private float cameraPitch;
|
||||
@Unique
|
||||
private float cameraYaw;
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public float freelook$getCameraPitch() {
|
||||
return cameraPitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public float freelook$getCameraYaw() {
|
||||
return cameraYaw;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public void freelook$setCameraPitch(float p) {
|
||||
cameraPitch = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public void freelook$setCameraYaw(float y) {
|
||||
cameraYaw = y;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
|
||||
import org.saturnclient.feature.features.ZoomFeature;
|
||||
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(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
|
||||
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
|
||||
private void onGetFov(CallbackInfoReturnable<Float> cir) {
|
||||
if (ZoomFeature.shouldZoom()) {
|
||||
cir.setReturnValue(cir.getReturnValue() / ZoomFeature.getZoomLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.modules.HealthRenderState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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)
|
||||
public class LivingEntityRenderStateMixin implements HealthRenderState {
|
||||
|
||||
@Unique private float saturn$health = 0f;
|
||||
@Unique private float saturn$maxHealth = 1f;
|
||||
@Unique private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER;
|
||||
|
||||
@Override public float saturn$getHealth() { return saturn$health; }
|
||||
@Override public float saturn$getMaxHealth() { return saturn$maxHealth; }
|
||||
|
||||
@Override
|
||||
public void saturn$setHealth(float health, float maxHealth) {
|
||||
this.saturn$health = health;
|
||||
this.saturn$maxHealth = maxHealth;
|
||||
}
|
||||
|
||||
@Override public EntityModule.EntityType saturn$getEntityType() { return saturn$entityType; }
|
||||
@Override public void saturn$setEntityType(EntityModule.EntityType type) { saturn$entityType = type; }
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.feature.features.NametagsFeature;
|
||||
import org.saturnclient.modules.HealthRenderState;
|
||||
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;
|
||||
|
||||
@Mixin(EntityRenderer.class)
|
||||
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
|
||||
private static final ThreadLocal<Boolean> saturn$rendering = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
@Shadow
|
||||
protected abstract <S extends EntityRenderState> void renderLabelIfPresent(
|
||||
S state, Text text, MatrixStack matrices,
|
||||
VertexConsumerProvider vertexConsumers, int light);
|
||||
|
||||
@Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true)
|
||||
private <S extends EntityRenderState> void saturn$replaceNametag(
|
||||
S state, Text text,
|
||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers,
|
||||
int light, CallbackInfo ci) {
|
||||
|
||||
if (saturn$rendering.get())
|
||||
return;
|
||||
if (!NametagsFeature.shouldReplaceName())
|
||||
return;
|
||||
if (!(state instanceof LivingEntityRenderState liv))
|
||||
return;
|
||||
|
||||
String replacement = NametagsFeature.getNametagString(
|
||||
new RenderStateEntityState(liv));
|
||||
if (replacement == null)
|
||||
return;
|
||||
|
||||
ci.cancel();
|
||||
saturn$rendering.set(true);
|
||||
try {
|
||||
renderLabelIfPresent(state, Text.literal(replacement),
|
||||
matrices, vertexConsumers, light);
|
||||
} finally {
|
||||
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,57 +0,0 @@
|
||||
package org.saturnclient.modules.mixins;
|
||||
|
||||
import org.saturnclient.feature.FeatureLayout;
|
||||
import org.saturnclient.feature.FeatureManager;
|
||||
import org.saturnclient.feature.HudFeature;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.RenderScopeImpl;
|
||||
import org.saturnclient.ui.SaturnScreenFabric;
|
||||
import org.saturnclient.ui.screens.HudEditor;
|
||||
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 net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.hud.InGameHud;
|
||||
import net.minecraft.client.render.RenderTickCounter;
|
||||
|
||||
@Mixin(InGameHud.class)
|
||||
public class RenderMixin {
|
||||
@Inject(method = "renderChat", at = @At("TAIL"))
|
||||
public void renderMods(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||
TextRenderer textRenderer = SaturnClient.client.textRenderer;
|
||||
|
||||
if (textRenderer != null &&
|
||||
!(SaturnClient.client.currentScreen instanceof SaturnScreenFabric f && f.screen instanceof HudEditor)) {
|
||||
RenderScope renderScope = new RenderScopeImpl(context.getMatrices(),
|
||||
((DrawContextAccessor) context).getVertexConsumers());
|
||||
|
||||
for (org.saturnclient.feature.Feature m : FeatureManager.ENABLED_MODS) {
|
||||
if (m instanceof HudFeature && m.isEnabled()) {
|
||||
FeatureLayout dim = ((HudFeature) m).getDimensions();
|
||||
|
||||
renderScope.getMatrixStack().push();
|
||||
|
||||
renderScope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0);
|
||||
|
||||
renderScope.getMatrixStack().scale(dim.scale.value, dim.scale.value, 1.0f);
|
||||
|
||||
if (dim.renderBackground) {
|
||||
renderScope.drawRoundedRectangle(0, 0, dim.width, dim.height, dim.radius.value,
|
||||
dim.bgColor.value);
|
||||
}
|
||||
|
||||
((HudFeature) m).renderHud(renderScope);
|
||||
|
||||
renderScope.getMatrixStack().pop();
|
||||
} else {
|
||||
m.render(renderScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,12 @@
|
||||
"package": "org.saturnclient.modules.mixins",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"CameraMixin",
|
||||
"EntityMixin",
|
||||
"RenderMixin",
|
||||
"LightmapTextureManagerMixin",
|
||||
"BackgroundRendererMixin",
|
||||
"GameRendererMixin",
|
||||
"NametagsMixin",
|
||||
"LivingEntityRenderStateMixin",
|
||||
"LightmapTextureManagerMixin",
|
||||
"LivingEntityRendererMixin",
|
||||
"LivingEntityRenderStateMixin",
|
||||
"NametagsMixin",
|
||||
"RenderMixin",
|
||||
"TpsMixin"
|
||||
],
|
||||
"injectors": {
|
||||
|
||||
Reference in New Issue
Block a user