Merge pull request #56 from saturnclientmc/refactor-mod-system

Refactor mod system #55
This commit is contained in:
Kr4ight
2026-03-24 13:10:43 +01:00
committed by GitHub
43 changed files with 407 additions and 401 deletions
+1 -1
Submodule common updated: 174c95240c...6873f76c43
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.entity; package org.saturnclient.impl.features.entity;
public interface CameraOverriddenEntity { public interface CameraOverriddenEntity {
float freelook$getCameraPitch(); float freelook$getCameraPitch();
@@ -1,4 +1,6 @@
package org.saturnclient.impl.modules.entity; package org.saturnclient.impl.features.entity;
import org.saturnclient.common.feature.EntityFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -6,11 +8,8 @@ import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import org.saturnclient.common.module.EntityModule;
/** /**
* Fabric implementation of {@link EntityModule}. * Fabric implementation of {@link EntityFeature}.
* *
* Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}. * Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}.
* Entity state for nametag rendering is supplied at render time by * Entity state for nametag rendering is supplied at render time by
@@ -18,11 +17,11 @@ import org.saturnclient.common.module.EntityModule;
* writes into the render state; {@link NametagsMixin} then constructs * writes into the render state; {@link NametagsMixin} then constructs
* a {@link RenderStateEntityState} from the already-extracted snapshot. * a {@link RenderStateEntityState} from the already-extracted snapshot.
*/ */
public class EntityModuleFabric implements EntityModule { public class EntityFeatureImpl implements EntityFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public EntityModuleFabric(MinecraftClient mc) { public EntityFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -0,0 +1,24 @@
package org.saturnclient.impl.features.entity;
import org.saturnclient.common.feature.EntityFeature;
/**
* 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);
EntityFeature.EntityType saturn$getEntityType();
void saturn$setEntityType(EntityFeature.EntityType type);
}
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules.mixins.entity; package org.saturnclient.impl.features.mixins.entity;
import org.saturnclient.feature.features.FreelookFeature; import org.saturnclient.impl.features.entity.CameraOverriddenEntity;
import org.saturnclient.impl.modules.entity.CameraOverriddenEntity; import org.saturnclient.mod.mods.FreelookMod;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -26,7 +26,7 @@ public abstract class CameraMixin {
@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, public void lockRotation(BlockView focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing,
float tickDelta, CallbackInfo ci) { float tickDelta, CallbackInfo ci) {
if (FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) { if (FreelookMod.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity; CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity;
if (firstTime && SaturnClient.client.player != null) { if (firstTime && SaturnClient.client.player != null) {
@@ -38,7 +38,7 @@ public abstract class CameraMixin {
cameraOverriddenEntity.freelook$getCameraPitch()); cameraOverriddenEntity.freelook$getCameraPitch());
} }
if (!FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) { if (!FreelookMod.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
firstTime = true; firstTime = true;
} }
} }
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules.mixins.entity; package org.saturnclient.impl.features.mixins.entity;
import org.saturnclient.feature.features.FreelookFeature; import org.saturnclient.impl.features.entity.CameraOverriddenEntity;
import org.saturnclient.impl.modules.entity.CameraOverriddenEntity; import org.saturnclient.mod.mods.FreelookMod;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -24,7 +24,7 @@ public class EntityMixin implements CameraOverriddenEntity {
public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) { public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) {
// noinspection ConstantValue// IntelliJ is incorrect here, this code block is // noinspection ConstantValue// IntelliJ is incorrect here, this code block is
// reachable // reachable
if (FreelookFeature.isFreeLooking && (Object) this instanceof ClientPlayerEntity) { if (FreelookMod.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
double pitchDelta = (yDelta * 0.15); double pitchDelta = (yDelta * 0.15);
double yawDelta = (xDelta * 0.15); double yawDelta = (xDelta * 0.15);
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.mixins.misc; package org.saturnclient.impl.features.mixins.misc;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRenderer;
@@ -7,10 +7,10 @@ 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.NametagsFeature; import org.saturnclient.impl.features.entity.EntityFeatureImpl;
import org.saturnclient.impl.modules.entity.EntityModuleFabric; import org.saturnclient.impl.features.entity.HealthRenderState;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.mixins.render.LivingEntityRenderStateMixin;
import org.saturnclient.impl.modules.mixins.render.LivingEntityRenderStateMixin; import org.saturnclient.mod.mods.NametagsMod;
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;
@@ -20,17 +20,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** /**
* Intercepts nametag rendering and replaces the displayed text with * Intercepts nametag rendering and replaces the displayed text with
* the string produced by {@link NametagsFeature#getNametagString}. * the string produced by {@link NametagsMod#getNametagString}.
* *
* Key changes from the original: * Key changes from the original:
* *
* <ul> * <ul>
* <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is * <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is
* replaced by {@link EntityModuleFabric.RenderStateEntityState}, * replaced by {@link EntityFeatureImpl.RenderStateEntityState},
* which wraps the {@link HealthRenderState} already injected into * which wraps the {@link HealthRenderState} already injected into
* the render state by {@link LivingEntityRenderStateMixin}.</li> * the render state by {@link LivingEntityRenderStateMixin}.</li>
* <li>The feature import changes from {@code Nametags} to * <li>The feature import changes from {@code Nametags} to
* {@link NametagsFeature}; the two static methods * {@link NametagsMod}; the two static methods
* ({@code shouldReplaceName} / {@code getNametagString}) are * ({@code shouldReplaceName} / {@code getNametagString}) are
* unchanged in signature.</li> * unchanged in signature.</li>
* <li>The entity's display name is extracted from the render state's * <li>The entity's display name is extracted from the render state's
@@ -64,7 +64,7 @@ public abstract class NametagsMixin {
if (saturn$rendering.get()) if (saturn$rendering.get())
return; return;
if (!NametagsFeature.shouldReplaceName()) if (!NametagsMod.shouldReplaceName())
return; return;
// Only living entities carry health data // Only living entities carry health data
@@ -76,11 +76,11 @@ public abstract class NametagsMixin {
// Build the platform-neutral EntityState from the render-state snapshot // Build the platform-neutral EntityState from the render-state snapshot
// (health/type were written by LivingEntityRendererMixin at extract time). // (health/type were written by LivingEntityRendererMixin at extract time).
String customName = text != null ? text.getString() : null; String customName = text != null ? text.getString() : null;
EntityModuleFabric.RenderStateEntityState entityState = new EntityModuleFabric.RenderStateEntityState( EntityFeatureImpl.RenderStateEntityState entityState = new EntityFeatureImpl.RenderStateEntityState(
customName, hrs); customName, hrs);
// Ask the feature for the replacement string // Ask the feature for the replacement string
String replacement = NametagsFeature.getNametagString(entityState); String replacement = NametagsMod.getNametagString(entityState);
if (replacement == null) if (replacement == null)
return; // feature decided not to replace this entity return; // feature decided not to replace this entity
@@ -1,8 +1,8 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.BackgroundRenderer; import net.minecraft.client.render.BackgroundRenderer;
import org.saturnclient.feature.features.NoFogFeature; import org.saturnclient.mod.mods.NoFogMod;
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;
@@ -12,7 +12,7 @@ public class BackgroundRendererMixin {
// fog // fog
@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 (NoFogFeature.isActive()) { if (NoFogMod.isActive()) {
return viewDistance * 2.0f; return viewDistance * 2.0f;
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.GameRenderer;
import org.saturnclient.feature.features.ZoomFeature; import org.saturnclient.mod.mods.ZoomMod;
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;
/** /**
* Overrides the game's field-of-view when {@link ZoomFeature} is active. * Overrides the game's field-of-view when {@link ZoomMod} is active.
* *
* The old code checked both {@code Zoom.isZooming} and * The old code checked both {@code Zoom.isZooming} and
* {@code Zoom.shouldZoom()} separately. The refactored * {@code Zoom.shouldZoom()} separately. The refactored
* {@link ZoomFeature#shouldZoom()} already ORs both conditions * {@link ZoomMod#shouldZoom()} already ORs both conditions
* ({@code enabled.value && isZooming}), so only one check is needed. * ({@code enabled.value && isZooming}), so only one check is needed.
* *
* No {@link org.saturnclient.common.module.ModuleProvider} is required * No {@link org.saturnclient.common.module.ModProvider} is required
* here because zooming is a pure render-time override with no per-tick * here because zooming is a pure render-time override with no per-tick
* engine queries. * engine queries.
*/ */
@@ -29,8 +29,8 @@ 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 (ZoomFeature.shouldZoom()) { if (ZoomMod.shouldZoom()) {
float zoomedFov = cir.getReturnValue() / ZoomFeature.getZoomLevel(); float zoomedFov = cir.getReturnValue() / ZoomMod.getZoomLevel();
cir.setReturnValue(zoomedFov); cir.setReturnValue(zoomedFov);
} }
} }
@@ -1,34 +1,34 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
// Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils // Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils
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.FullbrightFeature; import org.saturnclient.mod.mods.FullbrightMod;
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;
/** /**
* Overrides the lightmap's gamma computation when * Overrides the lightmap's gamma computation when
* {@link FullbrightFeature} is active, replacing the vanilla * {@link FullbrightMod} is active, replacing the vanilla
* brightness with a user-configured value. * brightness with a user-configured value.
* *
* The old import was {@code org.saturnclient.feature.features.Fullbright}; * The old import was {@code org.saturnclient.Mod.Mods.Fullbright};
* it is now {@link FullbrightFeature}. The injection target and * it is now {@link FullbrightMod}. The injection target and
* strategy are unchanged. * strategy are unchanged.
*/ */
@Mixin(LightmapTextureManager.class) @Mixin(LightmapTextureManager.class)
public class LightmapTextureManagerMixin { public class LightmapTextureManagerMixin {
/** /**
* If fullbright is active, substitute the feature's brightness * If fullbright is active, substitute the Mod's brightness
* value for whatever vanilla calculated. * value for whatever vanilla calculated.
*/ */
@ModifyExpressionValue(method = "update", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) @ModifyExpressionValue(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 (FullbrightFeature.shouldOverrideBrightness()) { if (FullbrightMod.shouldOverrideBrightness()) {
return FullbrightFeature.getBrightnessValue(); return FullbrightMod.getBrightnessValue();
} }
return original; return original;
} }
@@ -1,10 +1,10 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.common.module.EntityModule; import org.saturnclient.common.feature.EntityFeature;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.entity.HealthRenderState;
import org.saturnclient.impl.modules.mixins.misc.NametagsMixin; import org.saturnclient.impl.features.mixins.misc.NametagsMixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.Unique;
* storage into every living entity's render-state snapshot. * storage into every living entity's render-state snapshot.
* *
* Previously imported {@code NametagsInterface.EntityType}; the enum * Previously imported {@code NametagsInterface.EntityType}; the enum
* now lives in {@link EntityModule.EntityType} which is the single * now lives in {@link EntityFeature.EntityType} which is the single
* canonical definition shared by the mixin layer and the feature layer. * canonical definition shared by the mixin layer and the feature layer.
* *
* The stored values are written by * The stored values are written by
@@ -30,7 +30,7 @@ public class LivingEntityRenderStateMixin implements HealthRenderState {
@Unique @Unique
private float saturn$maxHealth = 1f; private float saturn$maxHealth = 1f;
@Unique @Unique
private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER; private EntityFeature.EntityType saturn$entityType = EntityFeature.EntityType.OTHER;
@Override @Override
public float saturn$getHealth() { public float saturn$getHealth() {
@@ -49,12 +49,12 @@ public class LivingEntityRenderStateMixin implements HealthRenderState {
} }
@Override @Override
public EntityModule.EntityType saturn$getEntityType() { public EntityFeature.EntityType saturn$getEntityType() {
return saturn$entityType; return saturn$entityType;
} }
@Override @Override
public void saturn$setEntityType(EntityModule.EntityType type) { public void saturn$setEntityType(EntityFeature.EntityType type) {
saturn$entityType = type; saturn$entityType = type;
} }
} }
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
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;
@@ -7,8 +7,8 @@ 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.common.feature.EntityFeature;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.entity.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;
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* them without touching the live entity again. * them without touching the live entity again.
* *
* Previously imported {@code NametagsInterface.EntityType}; now uses * Previously imported {@code NametagsInterface.EntityType}; now uses
* {@link EntityModule.EntityType} — the canonical enum shared across * {@link EntityFeature.EntityType} — the canonical enum shared across
* the whole module system. * the whole module system.
* *
* The injection target and strategy are unchanged from the original. * The injection target and strategy are unchanged from the original.
@@ -36,15 +36,15 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth()); hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth());
// Classify entity type for the nametag feature's filtering logic // Classify entity type for the nametag feature's filtering logic
EntityModule.EntityType type; EntityFeature.EntityType type;
if (entity instanceof PlayerEntity) if (entity instanceof PlayerEntity)
type = EntityModule.EntityType.PLAYER; type = EntityFeature.EntityType.PLAYER;
else if (entity instanceof HostileEntity) else if (entity instanceof HostileEntity)
type = EntityModule.EntityType.HOSTILE; type = EntityFeature.EntityType.HOSTILE;
else if (entity instanceof PassiveEntity) else if (entity instanceof PassiveEntity)
type = EntityModule.EntityType.PASSIVE; type = EntityFeature.EntityType.PASSIVE;
else else
type = EntityModule.EntityType.OTHER; type = EntityFeature.EntityType.OTHER;
hrs.saturn$setEntityType(type); hrs.saturn$setEntityType(type);
} }
@@ -1,16 +1,16 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter; 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.impl.ui.RenderScopeImpl; import org.saturnclient.impl.ui.RenderScopeImpl;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.mod.HudMod;
import org.saturnclient.mod.Mod;
import org.saturnclient.mod.ModLayout;
import org.saturnclient.mod.ModManager;
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;
@@ -24,9 +24,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* Drives per-frame rendering for all registered features. * Drives per-frame rendering for all registered features.
* *
* This mixin is intentionally kept thin: it creates a {@link RenderScope}, * This mixin is intentionally kept thin: it creates a {@link RenderScope},
* iterates the enabled feature list from {@link FeatureManager}, and * iterates the enabled feature list from {@link ModManager}, and
* dispatches either {@link HudFeature#renderHud} (for HUD elements) or * dispatches either {@link HudMod#renderHud} (for HUD elements) or
* {@link Feature#render} (for world-overlay features like Crosshair). * {@link Mod#render} (for world-overlay features like Crosshair).
* *
* No feature-specific logic lives here — all decisions about what to * No feature-specific logic lives here — all decisions about what to
* draw are made inside each feature class. * draw are made inside each feature class.
@@ -57,12 +57,12 @@ public class RenderMixin {
context.getMatrices(), context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers()); ((DrawContextAccessor) context).getVertexConsumers());
for (Feature feature : FeatureManager.ENABLED_MODS) { for (Mod feature : ModManager.ENABLED_MODS) {
if (!feature.isEnabled()) if (!feature.isEnabled())
continue; continue;
if (feature instanceof HudFeature hud) { if (feature instanceof HudMod hud) {
FeatureLayout dim = hud.getDimensions(); ModLayout dim = hud.getDimensions();
scope.getMatrixStack().push(); scope.getMatrixStack().push();
scope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0f); scope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0f);
@@ -77,7 +77,7 @@ public class RenderMixin {
scope.getMatrixStack().pop(); scope.getMatrixStack().pop();
} else { } else {
// Non-HUD features (e.g. CrosshairFeature) use the plain render hook. // Non-HUD features (e.g. CrosshairMod) use the plain render hook.
feature.render(scope); feature.render(scope);
} }
} }
@@ -1,30 +1,30 @@
package org.saturnclient.impl.modules.mixins.world; package org.saturnclient.impl.features.mixins.world;
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.TpsFeature; import org.saturnclient.mod.mods.TpsMod;
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;
/** /**
* Feeds world-time packets into {@link TpsFeature}'s TPS estimator. * Feeds world-time packets into {@link TpsMod}'s TPS estimator.
* *
* This is the only place in the entire codebase that reads a network * This is the only place in the entire codebase that reads a network
* packet and passes data to a feature. The feature's * packet and passes data to a feature. The feature's
* {@link TpsFeature#onTimePacket(long)} method handles all averaging * {@link TpsMod#onTimePacket(long)} method handles all averaging
* logic; the mixin is intentionally reduced to a single forwarding call. * logic; the mixin is intentionally reduced to a single forwarding call.
* *
* The only change from the original is renaming the import from * The only change from the original is renaming the import from
* {@code feature.features.Tps} to {@link TpsFeature}. * {@code feature.features.Tps} to {@link TpsMod}.
*/ */
@Mixin(ClientPlayNetworkHandler.class) @Mixin(ClientPlayNetworkHandler.class)
public class TpsMixin { 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) {
TpsFeature.onTimePacket(packet.time()); TpsMod.onTimePacket(packet.time());
} }
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.network; package org.saturnclient.impl.features.network;
import org.saturnclient.common.feature.NetworkFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.network.PlayerListEntry;
import org.saturnclient.common.module.NetworkModule;
/** /**
* Fabric implementation of {@link NetworkModule}. * Fabric implementation of {@link NetworkFeature}.
* *
* Connection presence is checked via the network handler. * Connection presence is checked via the network handler.
* Ping is read from the player's {@link PlayerListEntry} which the * Ping is read from the player's {@link PlayerListEntry} which the
* server populates; returns {@code -1} when unavailable. * server populates; returns {@code -1} when unavailable.
*/ */
public class NetworkModuleFabric implements NetworkModule { public class NetworkFeatureImpl implements NetworkFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public NetworkModuleFabric(MinecraftClient mc) { public NetworkFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.player; package org.saturnclient.impl.features.player;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
@@ -8,7 +8,7 @@ import net.minecraft.item.Items;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.saturnclient.common.module.PlayerModule; import org.saturnclient.common.feature.PlayerFeature;
import org.saturnclient.common.ref.game.EffectRef; import org.saturnclient.common.ref.game.EffectRef;
import org.saturnclient.common.ref.game.ItemStackRef; import org.saturnclient.common.ref.game.ItemStackRef;
@@ -17,20 +17,20 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* Fabric implementation of {@link PlayerModule}. * Fabric implementation of {@link PlayerFeature}.
* *
* All access to {@link MinecraftClient} and {@link ClientPlayerEntity} * All access to {@link MinecraftClient} and {@link ClientPlayerEntity}
* is encapsulated here. Features only ever see the {@link PlayerModule} * is encapsulated here. Features only ever see the {@link PlayerFeature}
* interface; they never import Minecraft classes directly. * interface; they never import Minecraft classes directly.
* *
* Every method guards against a missing player and returns a safe * Every method guards against a missing player and returns a safe
* default so features are free of null checks. * default so features are free of null checks.
*/ */
public class PlayerModuleFabric implements PlayerModule { public class PlayerFeatureImpl implements PlayerFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public PlayerModuleFabric(MinecraftClient mc) { public PlayerFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.render; package org.saturnclient.impl.features.render;
import org.saturnclient.common.feature.RenderFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.Perspective; import net.minecraft.client.option.Perspective;
import org.saturnclient.common.module.RenderModule;
/** /**
* Fabric implementation of {@link RenderModule}. * Fabric implementation of {@link RenderFeature}.
* *
* Window dimensions and FPS are read directly from * Window dimensions and FPS are read directly from
* {@link MinecraftClient}. Camera perspective control delegates to * {@link MinecraftClient}. Camera perspective control delegates to
* {@link net.minecraft.client.option.GameOptions}. * {@link net.minecraft.client.option.GameOptions}.
*/ */
public class RenderModuleFabric implements RenderModule { public class RenderFeatureImpl implements RenderFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public RenderModuleFabric(MinecraftClient mc) { public RenderFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,20 +1,20 @@
package org.saturnclient.impl.modules.world; package org.saturnclient.impl.features.world;
import org.saturnclient.common.feature.WorldFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.module.WorldModule;
/** /**
* Fabric implementation of {@link WorldModule}. * Fabric implementation of {@link WorldFeature}.
* *
* Reads the client world's time directly from {@link MinecraftClient}. * Reads the client world's time directly from {@link MinecraftClient}.
* Returns safe defaults when no world is loaded. * Returns safe defaults when no world is loaded.
*/ */
public class WorldModuleFabric implements WorldModule { public class WorldFeatureImpl implements WorldFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public WorldModuleFabric(MinecraftClient mc) { public WorldFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,20 +0,0 @@
package org.saturnclient.impl.modules.entity;
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);
}
@@ -0,0 +1,68 @@
package org.saturnclient.impl.provider;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.feature.*;
import org.saturnclient.common.provider.FeatureProvider;
import org.saturnclient.impl.features.entity.EntityFeatureImpl;
import org.saturnclient.impl.features.network.NetworkFeatureImpl;
import org.saturnclient.impl.features.player.PlayerFeatureImpl;
import org.saturnclient.impl.features.render.RenderFeatureImpl;
import org.saturnclient.impl.features.world.WorldFeatureImpl;
/**
* Fabric implementation of {@link FeatureProvider}.
*
* Instantiated once during client startup (e.g. in the mod initialiser)
* and passed to every feature constructor. All five sub-modules share
* the same {@link MinecraftClient} reference so there is no
* inconsistency between modules reading state within the same tick.
*
* <pre>{@code
* FeatureProvider provider = new FeatureProviderFabric(MinecraftClient.getInstance());
* FeatureManager.register(new AutoSprintFeature(provider));
* FeatureManager.register(new CoordinatesFeature(provider));
* // …
* }</pre>
*/
public class FeatureProviderFabric implements FeatureProvider {
private final PlayerFeature player;
private final WorldFeature world;
private final EntityFeature entity;
private final RenderFeature render;
private final NetworkFeature network;
public FeatureProviderFabric(MinecraftClient mc) {
this.player = new PlayerFeatureImpl(mc);
this.world = new WorldFeatureImpl(mc);
this.entity = new EntityFeatureImpl(mc);
this.render = new RenderFeatureImpl(mc);
this.network = new NetworkFeatureImpl(mc);
}
@Override
public PlayerFeature player() {
return player;
}
@Override
public WorldFeature world() {
return world;
}
@Override
public EntityFeature entity() {
return entity;
}
@Override
public RenderFeature render() {
return render;
}
@Override
public NetworkFeature network() {
return network;
}
}
@@ -1,68 +0,0 @@
package org.saturnclient.impl.provider;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.provider.ModuleProvider;
import org.saturnclient.impl.modules.entity.EntityModuleFabric;
import org.saturnclient.impl.modules.network.NetworkModuleFabric;
import org.saturnclient.impl.modules.player.PlayerModuleFabric;
import org.saturnclient.impl.modules.render.RenderModuleFabric;
import org.saturnclient.impl.modules.world.WorldModuleFabric;
import org.saturnclient.common.module.*;
/**
* Fabric implementation of {@link ModuleProvider}.
*
* Instantiated once during client startup (e.g. in the mod initialiser)
* and passed to every feature constructor. All five sub-modules share
* the same {@link MinecraftClient} reference so there is no
* inconsistency between modules reading state within the same tick.
*
* <pre>{@code
* ModuleProvider provider = new ModuleProviderFabric(MinecraftClient.getInstance());
* FeatureManager.register(new AutoSprintFeature(provider));
* FeatureManager.register(new CoordinatesFeature(provider));
* // …
* }</pre>
*/
public class ModuleProviderFabric implements ModuleProvider {
private final PlayerModule player;
private final WorldModule world;
private final EntityModule entity;
private final RenderModule render;
private final NetworkModule network;
public ModuleProviderFabric(MinecraftClient mc) {
this.player = new PlayerModuleFabric(mc);
this.world = new WorldModuleFabric(mc);
this.entity = new EntityModuleFabric(mc);
this.render = new RenderModuleFabric(mc);
this.network = new NetworkModuleFabric(mc);
}
@Override
public PlayerModule player() {
return player;
}
@Override
public WorldModule world() {
return world;
}
@Override
public EntityModule entity() {
return entity;
}
@Override
public RenderModule render() {
return render;
}
@Override
public NetworkModule network() {
return network;
}
}
@@ -8,15 +8,15 @@ import net.minecraft.client.MinecraftClient;
import org.saturnclient.client.ServiceClient; import org.saturnclient.client.ServiceClient;
import org.saturnclient.cosmetics.Emotes; import org.saturnclient.cosmetics.Emotes;
import org.saturnclient.cosmetics.Hats; import org.saturnclient.cosmetics.Hats;
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.GLFWProviderImpl; import org.saturnclient.impl.provider.GLFWProviderImpl;
import org.saturnclient.impl.provider.ModuleProviderFabric; import org.saturnclient.impl.provider.FeatureProviderFabric;
import org.saturnclient.impl.provider.RefConstructorImpl; import org.saturnclient.impl.provider.RefConstructorImpl;
import org.saturnclient.impl.provider.SaturnProviderImpl; import org.saturnclient.impl.provider.SaturnProviderImpl;
import org.saturnclient.impl.ui.EntityDrawerImpl; import org.saturnclient.impl.ui.EntityDrawerImpl;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.mod.ModManager;
import org.saturnclient.saturnclient.event.KeyInputHandler; import org.saturnclient.saturnclient.event.KeyInputHandler;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.ConfigManager; import org.saturnclient.config.ConfigManager;
@@ -41,11 +41,11 @@ public class SaturnClient implements ModInitializer {
Providers.saturn = new SaturnProviderImpl(); Providers.saturn = new SaturnProviderImpl();
Providers.refConstructor = new RefConstructorImpl(); Providers.refConstructor = new RefConstructorImpl();
Providers.GLFW = new GLFWProviderImpl(); Providers.GLFW = new GLFWProviderImpl();
Providers.module = new ModuleProviderFabric(client); Providers.feature = new FeatureProviderFabric(client);
SkinPreview.DRAWER = new EntityDrawerImpl(); SkinPreview.DRAWER = new EntityDrawerImpl();
Config.init(); Config.init();
FeatureManager.init(); ModManager.init();
client.execute(() -> { client.execute(() -> {
SaturnScreenFabric.preload(client); SaturnScreenFabric.preload(client);
@@ -54,7 +54,7 @@ public class SaturnClient implements ModInitializer {
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save()); ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
for (org.saturnclient.feature.Feature m : FeatureManager.ENABLED_MODS) { for (org.saturnclient.mod.Mod m : ModManager.ENABLED_MODS) {
m.tick(); m.tick();
} }
}); });
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.entity; package org.saturnclient.impl.features.entity;
public interface CameraOverriddenEntity { public interface CameraOverriddenEntity {
float freelook$getCameraPitch(); float freelook$getCameraPitch();
@@ -1,4 +1,6 @@
package org.saturnclient.impl.modules.entity; package org.saturnclient.impl.features.entity;
import org.saturnclient.common.feature.EntityFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -6,11 +8,8 @@ import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import org.saturnclient.common.module.EntityModule;
/** /**
* Fabric implementation of {@link EntityModule}. * Fabric implementation of {@link EntityFeature}.
* *
* Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}. * Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}.
* Entity state for nametag rendering is supplied at render time by * Entity state for nametag rendering is supplied at render time by
@@ -18,11 +17,11 @@ import org.saturnclient.common.module.EntityModule;
* writes into the render state; {@link NametagsMixin} then constructs * writes into the render state; {@link NametagsMixin} then constructs
* a {@link RenderStateEntityState} from the already-extracted snapshot. * a {@link RenderStateEntityState} from the already-extracted snapshot.
*/ */
public class EntityModuleFabric implements EntityModule { public class EntityFeatureImpl implements EntityFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public EntityModuleFabric(MinecraftClient mc) { public EntityFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -0,0 +1,24 @@
package org.saturnclient.impl.features.entity;
import org.saturnclient.common.feature.EntityFeature;
/**
* 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);
EntityFeature.EntityType saturn$getEntityType();
void saturn$setEntityType(EntityFeature.EntityType type);
}
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules.mixins.entity; package org.saturnclient.impl.features.mixins.entity;
import org.saturnclient.feature.features.FreelookFeature; import org.saturnclient.impl.features.entity.CameraOverriddenEntity;
import org.saturnclient.impl.modules.entity.CameraOverriddenEntity; import org.saturnclient.mod.mods.FreelookMod;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -26,7 +26,7 @@ public abstract class CameraMixin {
@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, public void lockRotation(BlockView focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing,
float tickDelta, CallbackInfo ci) { float tickDelta, CallbackInfo ci) {
if (FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) { if (FreelookMod.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity; CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity;
if (firstTime && SaturnClient.client.player != null) { if (firstTime && SaturnClient.client.player != null) {
@@ -38,7 +38,7 @@ public abstract class CameraMixin {
cameraOverriddenEntity.freelook$getCameraPitch()); cameraOverriddenEntity.freelook$getCameraPitch());
} }
if (!FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) { if (!FreelookMod.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
firstTime = true; firstTime = true;
} }
} }
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules.mixins.entity; package org.saturnclient.impl.features.mixins.entity;
import org.saturnclient.feature.features.FreelookFeature; import org.saturnclient.impl.features.entity.CameraOverriddenEntity;
import org.saturnclient.impl.modules.entity.CameraOverriddenEntity; import org.saturnclient.mod.mods.FreelookMod;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -24,7 +24,7 @@ public class EntityMixin implements CameraOverriddenEntity {
public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) { public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) {
// noinspection ConstantValue// IntelliJ is incorrect here, this code block is // noinspection ConstantValue// IntelliJ is incorrect here, this code block is
// reachable // reachable
if (FreelookFeature.isFreeLooking && (Object) this instanceof ClientPlayerEntity) { if (FreelookMod.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
double pitchDelta = (yDelta * 0.15); double pitchDelta = (yDelta * 0.15);
double yawDelta = (xDelta * 0.15); double yawDelta = (xDelta * 0.15);
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.mixins.misc; package org.saturnclient.impl.features.mixins.misc;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRenderer;
@@ -7,10 +7,10 @@ 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.NametagsFeature; import org.saturnclient.impl.features.entity.EntityFeatureImpl;
import org.saturnclient.impl.modules.entity.EntityModuleFabric; import org.saturnclient.impl.features.entity.HealthRenderState;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.mixins.render.LivingEntityRenderStateMixin;
import org.saturnclient.impl.modules.mixins.render.LivingEntityRenderStateMixin; import org.saturnclient.mod.mods.NametagsMod;
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;
@@ -20,17 +20,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** /**
* Intercepts nametag rendering and replaces the displayed text with * Intercepts nametag rendering and replaces the displayed text with
* the string produced by {@link NametagsFeature#getNametagString}. * the string produced by {@link NametagsMod#getNametagString}.
* *
* Key changes from the original: * Key changes from the original:
* *
* <ul> * <ul>
* <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is * <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is
* replaced by {@link EntityModuleFabric.RenderStateEntityState}, * replaced by {@link EntityFeatureImpl.RenderStateEntityState},
* which wraps the {@link HealthRenderState} already injected into * which wraps the {@link HealthRenderState} already injected into
* the render state by {@link LivingEntityRenderStateMixin}.</li> * the render state by {@link LivingEntityRenderStateMixin}.</li>
* <li>The feature import changes from {@code Nametags} to * <li>The feature import changes from {@code Nametags} to
* {@link NametagsFeature}; the two static methods * {@link NametagsMod}; the two static methods
* ({@code shouldReplaceName} / {@code getNametagString}) are * ({@code shouldReplaceName} / {@code getNametagString}) are
* unchanged in signature.</li> * unchanged in signature.</li>
* <li>The entity's display name is extracted from the render state's * <li>The entity's display name is extracted from the render state's
@@ -64,7 +64,7 @@ public abstract class NametagsMixin {
if (saturn$rendering.get()) if (saturn$rendering.get())
return; return;
if (!NametagsFeature.shouldReplaceName()) if (!NametagsMod.shouldReplaceName())
return; return;
// Only living entities carry health data // Only living entities carry health data
@@ -76,11 +76,11 @@ public abstract class NametagsMixin {
// Build the platform-neutral EntityState from the render-state snapshot // Build the platform-neutral EntityState from the render-state snapshot
// (health/type were written by LivingEntityRendererMixin at extract time). // (health/type were written by LivingEntityRendererMixin at extract time).
String customName = text != null ? text.getString() : null; String customName = text != null ? text.getString() : null;
EntityModuleFabric.RenderStateEntityState entityState = new EntityModuleFabric.RenderStateEntityState( EntityFeatureImpl.RenderStateEntityState entityState = new EntityFeatureImpl.RenderStateEntityState(
customName, hrs); customName, hrs);
// Ask the feature for the replacement string // Ask the feature for the replacement string
String replacement = NametagsFeature.getNametagString(entityState); String replacement = NametagsMod.getNametagString(entityState);
if (replacement == null) if (replacement == null)
return; // feature decided not to replace this entity return; // feature decided not to replace this entity
@@ -1,8 +1,8 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.BackgroundRenderer; import net.minecraft.client.render.BackgroundRenderer;
import org.saturnclient.feature.features.NoFogFeature; import org.saturnclient.mod.mods.NoFogMod;
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;
@@ -12,7 +12,7 @@ public class BackgroundRendererMixin {
// fog // fog
@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 (NoFogFeature.isActive()) { if (NoFogMod.isActive()) {
return viewDistance * 2.0f; return viewDistance * 2.0f;
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.GameRenderer;
import org.saturnclient.feature.features.ZoomFeature; import org.saturnclient.mod.mods.ZoomMod;
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;
/** /**
* Overrides the game's field-of-view when {@link ZoomFeature} is active. * Overrides the game's field-of-view when {@link ZoomMod} is active.
* *
* The old code checked both {@code Zoom.isZooming} and * The old code checked both {@code Zoom.isZooming} and
* {@code Zoom.shouldZoom()} separately. The refactored * {@code Zoom.shouldZoom()} separately. The refactored
* {@link ZoomFeature#shouldZoom()} already ORs both conditions * {@link ZoomMod#shouldZoom()} already ORs both conditions
* ({@code enabled.value && isZooming}), so only one check is needed. * ({@code enabled.value && isZooming}), so only one check is needed.
* *
* No {@link org.saturnclient.common.module.ModuleProvider} is required * No {@link org.saturnclient.common.module.ModProvider} is required
* here because zooming is a pure render-time override with no per-tick * here because zooming is a pure render-time override with no per-tick
* engine queries. * engine queries.
*/ */
@@ -29,8 +29,8 @@ 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 (ZoomFeature.shouldZoom()) { if (ZoomMod.shouldZoom()) {
float zoomedFov = cir.getReturnValue() / ZoomFeature.getZoomLevel(); float zoomedFov = cir.getReturnValue() / ZoomMod.getZoomLevel();
cir.setReturnValue(zoomedFov); cir.setReturnValue(zoomedFov);
} }
} }
@@ -1,21 +1,21 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
// Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils // Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils
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.FullbrightFeature; import org.saturnclient.mod.mods.FullbrightMod;
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;
/** /**
* Overrides the lightmap's gamma computation when * Overrides the lightmap's gamma computation when
* {@link FullbrightFeature} is active, replacing the vanilla * {@link FullbrightMod} is active, replacing the vanilla
* brightness with a user-configured value. * brightness with a user-configured value.
* *
* The old import was {@code org.saturnclient.feature.features.Fullbright}; * The old import was {@code org.saturnclient.feature.features.Fullbright};
* it is now {@link FullbrightFeature}. The injection target and * it is now {@link FullbrightMod}. The injection target and
* strategy are unchanged. * strategy are unchanged.
*/ */
@Mixin(LightmapTextureManager.class) @Mixin(LightmapTextureManager.class)
@@ -27,8 +27,8 @@ public class LightmapTextureManagerMixin {
*/ */
@ModifyExpressionValue(method = "update", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) @ModifyExpressionValue(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 (FullbrightFeature.shouldOverrideBrightness()) { if (FullbrightMod.shouldOverrideBrightness()) {
return FullbrightFeature.getBrightnessValue(); return FullbrightMod.getBrightnessValue();
} }
return original; return original;
} }
@@ -1,10 +1,10 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.common.module.EntityModule; import org.saturnclient.common.feature.EntityFeature;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.entity.HealthRenderState;
import org.saturnclient.impl.modules.mixins.misc.NametagsMixin; import org.saturnclient.impl.features.mixins.misc.NametagsMixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.Unique;
* storage into every living entity's render-state snapshot. * storage into every living entity's render-state snapshot.
* *
* Previously imported {@code NametagsInterface.EntityType}; the enum * Previously imported {@code NametagsInterface.EntityType}; the enum
* now lives in {@link EntityModule.EntityType} which is the single * now lives in {@link EntityFeature.EntityType} which is the single
* canonical definition shared by the mixin layer and the feature layer. * canonical definition shared by the mixin layer and the feature layer.
* *
* The stored values are written by * The stored values are written by
@@ -30,7 +30,7 @@ public class LivingEntityRenderStateMixin implements HealthRenderState {
@Unique @Unique
private float saturn$maxHealth = 1f; private float saturn$maxHealth = 1f;
@Unique @Unique
private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER; private EntityFeature.EntityType saturn$entityType = EntityFeature.EntityType.OTHER;
@Override @Override
public float saturn$getHealth() { public float saturn$getHealth() {
@@ -49,12 +49,12 @@ public class LivingEntityRenderStateMixin implements HealthRenderState {
} }
@Override @Override
public EntityModule.EntityType saturn$getEntityType() { public EntityFeature.EntityType saturn$getEntityType() {
return saturn$entityType; return saturn$entityType;
} }
@Override @Override
public void saturn$setEntityType(EntityModule.EntityType type) { public void saturn$setEntityType(EntityFeature.EntityType type) {
saturn$entityType = type; saturn$entityType = type;
} }
} }
@@ -1,4 +1,4 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
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;
@@ -7,8 +7,8 @@ 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.common.feature.EntityFeature;
import org.saturnclient.impl.modules.entity.HealthRenderState; import org.saturnclient.impl.features.entity.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;
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* them without touching the live entity again. * them without touching the live entity again.
* *
* Previously imported {@code NametagsInterface.EntityType}; now uses * Previously imported {@code NametagsInterface.EntityType}; now uses
* {@link EntityModule.EntityType} — the canonical enum shared across * {@link EntityFeature.EntityType} — the canonical enum shared across
* the whole module system. * the whole module system.
* *
* The injection target and strategy are unchanged from the original. * The injection target and strategy are unchanged from the original.
@@ -36,15 +36,15 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth()); hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth());
// Classify entity type for the nametag feature's filtering logic // Classify entity type for the nametag feature's filtering logic
EntityModule.EntityType type; EntityFeature.EntityType type;
if (entity instanceof PlayerEntity) if (entity instanceof PlayerEntity)
type = EntityModule.EntityType.PLAYER; type = EntityFeature.EntityType.PLAYER;
else if (entity instanceof HostileEntity) else if (entity instanceof HostileEntity)
type = EntityModule.EntityType.HOSTILE; type = EntityFeature.EntityType.HOSTILE;
else if (entity instanceof PassiveEntity) else if (entity instanceof PassiveEntity)
type = EntityModule.EntityType.PASSIVE; type = EntityFeature.EntityType.PASSIVE;
else else
type = EntityModule.EntityType.OTHER; type = EntityFeature.EntityType.OTHER;
hrs.saturn$setEntityType(type); hrs.saturn$setEntityType(type);
} }
@@ -1,16 +1,16 @@
package org.saturnclient.impl.modules.mixins.render; package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter; 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.impl.ui.RenderScopeImpl; import org.saturnclient.impl.ui.RenderScopeImpl;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.mod.HudMod;
import org.saturnclient.mod.Mod;
import org.saturnclient.mod.ModLayout;
import org.saturnclient.mod.ModManager;
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;
@@ -24,9 +24,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* Drives per-frame rendering for all registered features. * Drives per-frame rendering for all registered features.
* *
* This mixin is intentionally kept thin: it creates a {@link RenderScope}, * This mixin is intentionally kept thin: it creates a {@link RenderScope},
* iterates the enabled feature list from {@link FeatureManager}, and * iterates the enabled feature list from {@link ModManager}, and
* dispatches either {@link HudFeature#renderHud} (for HUD elements) or * dispatches either {@link HudMod#renderHud} (for HUD elements) or
* {@link Feature#render} (for world-overlay features like Crosshair). * {@link Mod#render} (for world-overlay features like Crosshair).
* *
* No feature-specific logic lives here — all decisions about what to * No feature-specific logic lives here — all decisions about what to
* draw are made inside each feature class. * draw are made inside each feature class.
@@ -57,12 +57,12 @@ public class RenderMixin {
context.getMatrices(), context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers()); ((DrawContextAccessor) context).getVertexConsumers());
for (Feature feature : FeatureManager.ENABLED_MODS) { for (Mod feature : ModManager.ENABLED_MODS) {
if (!feature.isEnabled()) if (!feature.isEnabled())
continue; continue;
if (feature instanceof HudFeature hud) { if (feature instanceof HudMod hud) {
FeatureLayout dim = hud.getDimensions(); ModLayout dim = hud.getDimensions();
scope.getMatrixStack().push(); scope.getMatrixStack().push();
scope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0f); scope.getMatrixStack().translate(dim.x.value, (float) dim.y.value, 0f);
@@ -1,30 +1,30 @@
package org.saturnclient.impl.modules.mixins.world; package org.saturnclient.impl.features.mixins.world;
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.TpsFeature; import org.saturnclient.mod.mods.TpsMod;
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;
/** /**
* Feeds world-time packets into {@link TpsFeature}'s TPS estimator. * Feeds world-time packets into {@link TpsMod}'s TPS estimator.
* *
* This is the only place in the entire codebase that reads a network * This is the only place in the entire codebase that reads a network
* packet and passes data to a feature. The feature's * packet and passes data to a feature. The feature's
* {@link TpsFeature#onTimePacket(long)} method handles all averaging * {@link TpsMod#onTimePacket(long)} method handles all averaging
* logic; the mixin is intentionally reduced to a single forwarding call. * logic; the mixin is intentionally reduced to a single forwarding call.
* *
* The only change from the original is renaming the import from * The only change from the original is renaming the import from
* {@code feature.features.Tps} to {@link TpsFeature}. * {@code feature.features.Tps} to {@link TpsMod}.
*/ */
@Mixin(ClientPlayNetworkHandler.class) @Mixin(ClientPlayNetworkHandler.class)
public class TpsMixin { 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) {
TpsFeature.onTimePacket(packet.time()); TpsMod.onTimePacket(packet.time());
} }
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.network; package org.saturnclient.impl.features.network;
import org.saturnclient.common.feature.NetworkFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.network.PlayerListEntry;
import org.saturnclient.common.module.NetworkModule;
/** /**
* Fabric implementation of {@link NetworkModule}. * Fabric implementation of {@link NetworkFeature}.
* *
* Connection presence is checked via the network handler. * Connection presence is checked via the network handler.
* Ping is read from the player's {@link PlayerListEntry} which the * Ping is read from the player's {@link PlayerListEntry} which the
* server populates; returns {@code -1} when unavailable. * server populates; returns {@code -1} when unavailable.
*/ */
public class NetworkModuleFabric implements NetworkModule { public class NetworkFeatureImpl implements NetworkFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public NetworkModuleFabric(MinecraftClient mc) { public NetworkFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,15 +1,15 @@
package org.saturnclient.impl.modules.player; package org.saturnclient.impl.features.player;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.entity.EquipmentSlot;
import org.saturnclient.common.module.PlayerModule; import org.saturnclient.common.feature.PlayerFeature;
import org.saturnclient.common.ref.game.EffectRef; import org.saturnclient.common.ref.game.EffectRef;
import org.saturnclient.common.ref.game.ItemStackRef; import org.saturnclient.common.ref.game.ItemStackRef;
@@ -18,20 +18,20 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* Fabric implementation of {@link PlayerModule}. * Fabric implementation of {@link PlayerFeature}.
* *
* All access to {@link MinecraftClient} and {@link ClientPlayerEntity} * All access to {@link MinecraftClient} and {@link ClientPlayerEntity}
* is encapsulated here. Features only ever see the {@link PlayerModule} * is encapsulated here. Features only ever see the {@link PlayerFeature}
* interface; they never import Minecraft classes directly. * interface; they never import Minecraft classes directly.
* *
* Every method guards against a missing player and returns a safe * Every method guards against a missing player and returns a safe
* default so features are free of null checks. * default so features are free of null checks.
*/ */
public class PlayerModuleFabric implements PlayerModule { public class PlayerFeatureImpl implements PlayerFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public PlayerModuleFabric(MinecraftClient mc) { public PlayerFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,22 +1,22 @@
package org.saturnclient.impl.modules.render; package org.saturnclient.impl.features.render;
import org.saturnclient.common.feature.RenderFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.Perspective; import net.minecraft.client.option.Perspective;
import org.saturnclient.common.module.RenderModule;
/** /**
* Fabric implementation of {@link RenderModule}. * Fabric implementation of {@link RenderFeature}.
* *
* Window dimensions and FPS are read directly from * Window dimensions and FPS are read directly from
* {@link MinecraftClient}. Camera perspective control delegates to * {@link MinecraftClient}. Camera perspective control delegates to
* {@link net.minecraft.client.option.GameOptions}. * {@link net.minecraft.client.option.GameOptions}.
*/ */
public class RenderModuleFabric implements RenderModule { public class RenderFeatureImpl implements RenderFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public RenderModuleFabric(MinecraftClient mc) { public RenderFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,20 +1,20 @@
package org.saturnclient.impl.modules.world; package org.saturnclient.impl.features.world;
import org.saturnclient.common.feature.WorldFeature;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.module.WorldModule;
/** /**
* Fabric implementation of {@link WorldModule}. * Fabric implementation of {@link WorldFeature}.
* *
* Reads the client world's time directly from {@link MinecraftClient}. * Reads the client world's time directly from {@link MinecraftClient}.
* Returns safe defaults when no world is loaded. * Returns safe defaults when no world is loaded.
*/ */
public class WorldModuleFabric implements WorldModule { public class WorldFeatureImpl implements WorldFeature {
private final MinecraftClient mc; private final MinecraftClient mc;
public WorldModuleFabric(MinecraftClient mc) { public WorldFeatureImpl(MinecraftClient mc) {
this.mc = mc; this.mc = mc;
} }
@@ -1,20 +0,0 @@
package org.saturnclient.impl.modules.entity;
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);
}
@@ -0,0 +1,68 @@
package org.saturnclient.impl.provider;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.feature.*;
import org.saturnclient.common.provider.FeatureProvider;
import org.saturnclient.impl.features.entity.EntityFeatureImpl;
import org.saturnclient.impl.features.network.NetworkFeatureImpl;
import org.saturnclient.impl.features.player.PlayerFeatureImpl;
import org.saturnclient.impl.features.render.RenderFeatureImpl;
import org.saturnclient.impl.features.world.WorldFeatureImpl;
/**
* Fabric implementation of {@link FeatureProvider}.
*
* Instantiated once during client startup (e.g. in the mod initialiser)
* and passed to every feature constructor. All five sub-modules share
* the same {@link MinecraftClient} reference so there is no
* inconsistency between modules reading state within the same tick.
*
* <pre>{@code
* FeatureProvider provider = new FeatureProviderFabric(MinecraftClient.getInstance());
* FeatureManager.register(new AutoSprintFeature(provider));
* FeatureManager.register(new CoordinatesFeature(provider));
* // …
* }</pre>
*/
public class FeatureProviderFabric implements FeatureProvider {
private final PlayerFeature player;
private final WorldFeature world;
private final EntityFeature entity;
private final RenderFeature render;
private final NetworkFeature network;
public FeatureProviderFabric(MinecraftClient mc) {
this.player = new PlayerFeatureImpl(mc);
this.world = new WorldFeatureImpl(mc);
this.entity = new EntityFeatureImpl(mc);
this.render = new RenderFeatureImpl(mc);
this.network = new NetworkFeatureImpl(mc);
}
@Override
public PlayerFeature player() {
return player;
}
@Override
public WorldFeature world() {
return world;
}
@Override
public EntityFeature entity() {
return entity;
}
@Override
public RenderFeature render() {
return render;
}
@Override
public NetworkFeature network() {
return network;
}
}
@@ -1,68 +0,0 @@
package org.saturnclient.impl.provider;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.common.provider.ModuleProvider;
import org.saturnclient.impl.modules.entity.EntityModuleFabric;
import org.saturnclient.impl.modules.network.NetworkModuleFabric;
import org.saturnclient.impl.modules.player.PlayerModuleFabric;
import org.saturnclient.impl.modules.render.RenderModuleFabric;
import org.saturnclient.impl.modules.world.WorldModuleFabric;
import org.saturnclient.common.module.*;
/**
* Fabric implementation of {@link ModuleProvider}.
*
* Instantiated once during client startup (e.g. in the mod initialiser)
* and passed to every feature constructor. All five sub-modules share
* the same {@link MinecraftClient} reference so there is no
* inconsistency between modules reading state within the same tick.
*
* <pre>{@code
* ModuleProvider provider = new ModuleProviderFabric(MinecraftClient.getInstance());
* FeatureManager.register(new AutoSprintFeature(provider));
* FeatureManager.register(new CoordinatesFeature(provider));
* // …
* }</pre>
*/
public class ModuleProviderFabric implements ModuleProvider {
private final PlayerModule player;
private final WorldModule world;
private final EntityModule entity;
private final RenderModule render;
private final NetworkModule network;
public ModuleProviderFabric(MinecraftClient mc) {
this.player = new PlayerModuleFabric(mc);
this.world = new WorldModuleFabric(mc);
this.entity = new EntityModuleFabric(mc);
this.render = new RenderModuleFabric(mc);
this.network = new NetworkModuleFabric(mc);
}
@Override
public PlayerModule player() {
return player;
}
@Override
public WorldModule world() {
return world;
}
@Override
public EntityModule entity() {
return entity;
}
@Override
public RenderModule render() {
return render;
}
@Override
public NetworkModule network() {
return network;
}
}
@@ -8,15 +8,15 @@ import net.minecraft.client.MinecraftClient;
import org.saturnclient.client.ServiceClient; import org.saturnclient.client.ServiceClient;
import org.saturnclient.cosmetics.Emotes; import org.saturnclient.cosmetics.Emotes;
import org.saturnclient.cosmetics.Hats; import org.saturnclient.cosmetics.Hats;
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.GLFWProviderImpl; import org.saturnclient.impl.provider.GLFWProviderImpl;
import org.saturnclient.impl.provider.ModuleProviderFabric; import org.saturnclient.impl.provider.FeatureProviderFabric;
import org.saturnclient.impl.provider.RefConstructorImpl; import org.saturnclient.impl.provider.RefConstructorImpl;
import org.saturnclient.impl.provider.SaturnProviderImpl; import org.saturnclient.impl.provider.SaturnProviderImpl;
import org.saturnclient.impl.ui.EntityDrawerImpl; import org.saturnclient.impl.ui.EntityDrawerImpl;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.mod.ModManager;
import org.saturnclient.saturnclient.event.KeyInputHandler; import org.saturnclient.saturnclient.event.KeyInputHandler;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.ConfigManager; import org.saturnclient.config.ConfigManager;
@@ -41,11 +41,11 @@ public class SaturnClient implements ModInitializer {
Providers.saturn = new SaturnProviderImpl(); Providers.saturn = new SaturnProviderImpl();
Providers.refConstructor = new RefConstructorImpl(); Providers.refConstructor = new RefConstructorImpl();
Providers.GLFW = new GLFWProviderImpl(); Providers.GLFW = new GLFWProviderImpl();
Providers.module = new ModuleProviderFabric(client); Providers.feature = new FeatureProviderFabric(client);
SkinPreview.DRAWER = new EntityDrawerImpl(); SkinPreview.DRAWER = new EntityDrawerImpl();
Config.init(); Config.init();
FeatureManager.init(); ModManager.init();
client.execute(() -> { client.execute(() -> {
SaturnScreenFabric.preload(client); SaturnScreenFabric.preload(client);
@@ -54,7 +54,7 @@ public class SaturnClient implements ModInitializer {
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save()); ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
for (org.saturnclient.feature.Feature m : FeatureManager.ENABLED_MODS) { for (org.saturnclient.mod.Mod m : ModManager.ENABLED_MODS) {
m.tick(); m.tick();
} }
}); });