Refactor mixins

This commit is contained in:
2026-03-08 13:34:45 +01:00
parent f182ac7e91
commit 8854726916
27 changed files with 193 additions and 170 deletions
+1 -1
Submodule common updated: da6576e4ed...56d9be1e5a
@@ -1,7 +1,7 @@
package org.saturnclient.impl.modules;
import org.saturnclient.common.ref.game.ItemStackRef;
import org.saturnclient.feature.interfaces.ArmorDisplayInterface;
import org.saturnclient.feature.features.featuresinterfaces.ArmorDisplayInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.EquipmentSlot;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.AutoSprintInterface;
import org.saturnclient.feature.features.featuresinterfaces.AutoSprintInterface;
import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.CoordinatesInterface;
import org.saturnclient.feature.features.featuresinterfaces.CoordinatesInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.Vec3d;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.CrosshairInterface;
import org.saturnclient.feature.features.featuresinterfaces.CrosshairInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.DayCounterInterface;
import org.saturnclient.feature.features.featuresinterfaces.DayCounterInterface;
import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.FpsInterface;
import org.saturnclient.feature.features.featuresinterfaces.FpsInterface;
import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.FreelookInterface;
import org.saturnclient.feature.features.featuresinterfaces.FreelookInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.Perspective;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.HealthDisplayInterface;
import org.saturnclient.feature.features.featuresinterfaces.HealthDisplayInterface;
import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.KeystrokesInterface;
import org.saturnclient.feature.features.featuresinterfaces.KeystrokesInterface;
import net.minecraft.client.MinecraftClient;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.NametagsInterface;
import org.saturnclient.feature.features.featuresinterfaces.NametagsInterface;
import org.saturnclient.modules.HealthRenderState;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.PingInterface;
import org.saturnclient.feature.features.featuresinterfaces.PingInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
@@ -11,9 +11,11 @@ public class PingFabric implements PingInterface {
@Override
public int getPing() {
if (mc.player == null || mc.getNetworkHandler() == null) return 0;
if (mc.player == null || mc.getNetworkHandler() == null)
return 0;
ClientPlayNetworkHandler handler = mc.getNetworkHandler();
if (handler.getPlayerListEntry(mc.player.getUuid()) == null) return 0;
if (handler.getPlayerListEntry(mc.player.getUuid()) == null)
return 0;
return handler.getPlayerListEntry(mc.player.getUuid()).getLatency();
}
}
@@ -1,6 +1,6 @@
package org.saturnclient.impl.modules;
import org.saturnclient.feature.interfaces.SpeedometerInterface;
import org.saturnclient.feature.features.featuresinterfaces.SpeedometerInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
@@ -5,7 +5,7 @@ import java.util.stream.Collectors;
import java.util.Arrays;
import org.saturnclient.common.ref.asset.SpriteRef;
import org.saturnclient.feature.interfaces.StatusEffectsInterface;
import org.saturnclient.feature.features.featuresinterfaces.StatusEffectsInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.StatusEffectSpriteManager;
@@ -1,37 +0,0 @@
package org.saturnclient.impl.provider;
import org.saturnclient.feature.Feature;
import org.saturnclient.feature.FeatureManager.ModuleProvider;
import org.saturnclient.feature.features.*;
import org.saturnclient.impl.modules.*;
public class FabricModuleProvider implements ModuleProvider {
public static final Feature[] MODS = {
new ArmorDisplay(new ArmorDisplayFabric()),
new AutoSprint(new AutoSprintFabric()),
new Coordinates(new CoordinatesFabric()),
new Crosshair(new CrosshairFabric()),
new DayCounter(new DayCounterFabric()),
new Fps(new FpsFabric()),
new Freelook(new FreelookFabric()),
new HealthDisplay(new HealthDisplayFabric()),
new Keystrokes(new KeystrokesFabric()),
new Clock(),
new Fullbright(),
new Nametags(new NametagsFabric()),
new NoFog(),
new Ping(new PingFabric()),
new Speedometer(new SpeedometerFabric()),
new StatusEffects(new StatusEffectsFabric()),
new Tps(),
new Zoom()
};
@Override
public Feature[] getMods() {
return MODS;
}
}
@@ -1,6 +1,6 @@
package org.saturnclient.modules;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType;
import org.saturnclient.common.module.EntityModule.EntityType;
public interface HealthRenderState {
float saturn$getHealth();
@@ -3,7 +3,7 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.BackgroundRenderer;
import net.minecraft.entity.Entity;
import org.saturnclient.feature.features.NoFog;
import org.saturnclient.feature.features.NoFogFeature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@@ -11,22 +11,19 @@ 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 (NoFog.isActive()) {
return Float.MAX_VALUE; // this could be customised
if (NoFogFeature.isActive()) {
return Float.MAX_VALUE;
}
return viewDistance;
}
// liquids
@Redirect(
method = "applyFog",
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSpectator()Z")
)
@Redirect(method = "applyFog", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSpectator()Z"))
private static boolean redirectIsSpectator(Entity entity) {
if (NoFog.isActive() && NoFog.liquids()) {
return true; // pretend to be spectator
if (NoFogFeature.isActive() && NoFogFeature.liquids()) {
return true; // pretend to be spectator to bypass liquid fog
}
return entity.isSpectator();
}
@@ -6,7 +6,7 @@ import net.minecraft.client.render.Camera;
import net.minecraft.entity.Entity;
import net.minecraft.world.BlockView;
import org.saturnclient.feature.features.Freelook;
import org.saturnclient.feature.features.FreelookFeature;
import org.saturnclient.modules.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -17,28 +17,32 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Unique
boolean firstTime = true;
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 (Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity;
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) {
cameraOverriddenEntity.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch());
cameraOverriddenEntity.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
overridden.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch());
overridden.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
firstTime = false;
}
this.setRotation(cameraOverriddenEntity.freelook$getCameraYaw(), cameraOverriddenEntity.freelook$getCameraPitch());
this.setRotation(overridden.freelook$getCameraYaw(), overridden.freelook$getCameraPitch());
}
if (!Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
if (!FreelookFeature.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
firstTime = true;
}
}
}
}
@@ -1,11 +1,10 @@
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.Freelook;
import org.saturnclient.feature.features.FreelookFeature;
import org.saturnclient.modules.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -15,48 +14,45 @@ 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// IntelliJ is incorrect here, this code block is reachable
if (Freelook.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
double pitchDelta = (yDelta * 0.15);
double yawDelta = (xDelta * 0.15);
this.cameraPitch = MathHelper.clamp(this.cameraPitch + (float) pitchDelta, -90.0f, 90.0f);
this.cameraYaw += (float) yawDelta;
@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 this.cameraPitch;
}
@Override
@Unique
public float freelook$getCameraPitch() {
return cameraPitch;
}
@Override
@Unique
public float freelook$getCameraYaw() {
return this.cameraYaw;
}
@Override
@Unique
public float freelook$getCameraYaw() {
return cameraYaw;
}
@Override
@Unique
public void freelook$setCameraPitch(float pitch) {
this.cameraPitch = pitch;
}
@Override
@Unique
public void freelook$setCameraPitch(float p) {
cameraPitch = p;
}
@Override
@Unique
public void freelook$setCameraYaw(float yaw) {
this.cameraYaw = yaw;
}
}
@Override
@Unique
public void freelook$setCameraYaw(float y) {
cameraYaw = y;
}
}
@@ -1,22 +1,20 @@
package org.saturnclient.modules.mixins;
import org.saturnclient.feature.features.Zoom;
import net.minecraft.client.render.GameRenderer;
import org.saturnclient.feature.features.ZoomFeature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.render.GameRenderer;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
private void onGetFov(CallbackInfoReturnable<Float> cir) {
if (Zoom.isZooming && Zoom.shouldZoom()) {
float originalFov = cir.getReturnValue();
float zoomedFov = originalFov / Zoom.getZoomLevel();
cir.setReturnValue(zoomedFov);
if (ZoomFeature.shouldZoom()) {
cir.setReturnValue(cir.getReturnValue() / ZoomFeature.getZoomLevel());
}
}
}
@@ -4,20 +4,17 @@ package org.saturnclient.modules.mixins;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.render.LightmapTextureManager;
import org.saturnclient.feature.features.Fullbright;
import org.saturnclient.feature.features.FullbrightFeature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(LightmapTextureManager.class)
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) {
if (Fullbright.shouldOverrideBrightness()) {
return Fullbright.getBrightnessValue();
if (FullbrightFeature.shouldOverrideBrightness()) {
return FullbrightFeature.getBrightnessValue();
}
return original;
}
@@ -2,25 +2,33 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType;
import org.saturnclient.common.module.EntityModule;
import org.saturnclient.modules.HealthRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.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 org.saturnclient.modules.HealthRenderState {
public class LivingEntityRenderStateMixin implements HealthRenderState {
@Unique private float saturn$health = 0f;
@Unique private float saturn$health = 0f;
@Unique private float saturn$maxHealth = 1f;
@Unique private EntityType saturn$entityType = EntityType.OTHER;
@Unique private EntityModule.EntityType saturn$entityType = EntityModule.EntityType.OTHER;
public float saturn$getHealth() { return saturn$health; }
public float saturn$getMaxHealth() { return saturn$maxHealth; }
@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$health = health;
this.saturn$maxHealth = maxHealth;
}
public EntityType saturn$getEntityType() { return saturn$entityType; }
public void saturn$setEntityType(EntityType type) { this.saturn$entityType = type; }
}
@Override public EntityModule.EntityType saturn$getEntityType() { return saturn$entityType; }
@Override public void saturn$setEntityType(EntityModule.EntityType type) { saturn$entityType = type; }
}
@@ -2,19 +2,22 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import org.saturnclient.feature.interfaces.NametagsInterface.EntityType;
import org.saturnclient.modules.HealthRenderState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.HostileEntity;
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.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.
*/
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> {
@@ -25,14 +28,16 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
hrs.saturn$setHealth(entity.getHealth(), entity.getMaxHealth());
if (entity instanceof PlayerEntity) {
hrs.saturn$setEntityType(EntityType.PLAYER);
} else if (entity instanceof HostileEntity) {
hrs.saturn$setEntityType(EntityType.HOSTILE);
} else if (entity instanceof PassiveEntity) {
hrs.saturn$setEntityType(EntityType.PASSIVE);
} else {
hrs.saturn$setEntityType(EntityType.OTHER);
}
EntityModule.EntityType type;
if (entity instanceof PlayerEntity)
type = EntityModule.EntityType.PLAYER;
else if (entity instanceof HostileEntity)
type = EntityModule.EntityType.HOSTILE;
else if (entity instanceof PassiveEntity)
type = EntityModule.EntityType.PASSIVE;
else
type = EntityModule.EntityType.OTHER;
hrs.saturn$setEntityType(type);
}
}
@@ -7,8 +7,9 @@ import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import org.saturnclient.feature.features.Nametags;
import org.saturnclient.impl.modules.NametagsFabric;
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;
@@ -19,32 +20,85 @@ 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
);
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,
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 (!Nametags.shouldReplaceName()) return;
if (!(state instanceof LivingEntityRenderState living)) return;
if (saturn$rendering.get())
return;
if (!NametagsFeature.shouldReplaceName())
return;
if (!(state instanceof LivingEntityRenderState liv))
return;
String replacement = Nametags.getNametagString(new NametagsFabric.EntityStateImpl(living));
if (replacement == null) 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);
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,8 +1,8 @@
package org.saturnclient.modules.mixins;
import org.saturnclient.feature.HudFeature;
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;
@@ -3,7 +3,7 @@ package org.saturnclient.modules.mixins;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket;
import org.saturnclient.feature.features.Tps;
import org.saturnclient.feature.features.TpsFeature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -14,6 +14,6 @@ public class TpsMixin {
@Inject(method = "onWorldTimeUpdate", at = @At("HEAD"))
private void onWorldTimeUpdate(WorldTimeUpdateS2CPacket packet, CallbackInfo ci) {
Tps.onTimePacket(packet.time());
TpsFeature.onTimePacket(packet.time());
}
}
@@ -11,7 +11,6 @@ import org.saturnclient.cosmetics.Hats;
import org.saturnclient.feature.FeatureManager;
import org.saturnclient.cosmetics.Cloaks;
import org.saturnclient.common.provider.Providers;
import org.saturnclient.impl.provider.FabricModuleProvider;
import org.saturnclient.impl.provider.GLFWProviderImpl;
import org.saturnclient.impl.provider.RefConstructorImpl;
import org.saturnclient.impl.provider.SaturnProviderImpl;
@@ -44,7 +43,7 @@ public class SaturnClient implements ModInitializer {
SkinPreview.DRAWER = new EntityDrawerImpl();
Config.init();
FeatureManager.init(new FabricModuleProvider());
FeatureManager.init();
client.execute(() -> {
SaturnScreenFabric.preload(client);