Modules implementation
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.hit.EntityHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
|
||||
import org.saturnclient.common.module.EntityModule;
|
||||
import org.saturnclient.modules.HealthRenderState;
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link EntityModule}.
|
||||
*
|
||||
* Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}.
|
||||
* Entity state for nametag rendering is supplied at render time by
|
||||
* {@link org.saturnclient.modules.mixins.LivingEntityRendererMixin} which
|
||||
* writes into the render state; {@link NametagsMixin} then constructs
|
||||
* a {@link RenderStateEntityState} from the already-extracted snapshot.
|
||||
*/
|
||||
public class EntityModuleFabric implements EntityModule {
|
||||
|
||||
private final MinecraftClient mc;
|
||||
|
||||
public EntityModuleFabric(MinecraftClient mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetingLivingEntity() {
|
||||
HitResult target = mc.crosshairTarget;
|
||||
if (target == null || target.getType() != HitResult.Type.ENTITY)
|
||||
return false;
|
||||
Entity hit = ((EntityHitResult) target).getEntity();
|
||||
return hit instanceof LivingEntity;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Render-state adapter
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wraps a {@link HealthRenderState} (already injected into the
|
||||
* Minecraft render-state object) as a platform-neutral
|
||||
* {@link EntityState} for use by {@link NametagsFeature}.
|
||||
*
|
||||
* This is constructed inside {@link NametagsMixin} where both the
|
||||
* render-state mixin interface and the entity name are available.
|
||||
*/
|
||||
public static final class RenderStateEntityState implements EntityState {
|
||||
|
||||
private final String customName;
|
||||
private final HealthRenderState hrs;
|
||||
|
||||
public RenderStateEntityState(String customName, HealthRenderState hrs) {
|
||||
this.customName = customName;
|
||||
this.hrs = hrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCustomName() {
|
||||
return customName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHealth() {
|
||||
return hrs.saturn$getHealth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMaxHealth() {
|
||||
return hrs.saturn$getMaxHealth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return hrs.saturn$getEntityType();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
import org.saturnclient.common.provider.ModuleProvider;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
|
||||
import org.saturnclient.common.module.NetworkModule;
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link NetworkModule}.
|
||||
*
|
||||
* Connection presence is checked via the network handler.
|
||||
* Ping is read from the player's {@link PlayerListEntry} which the
|
||||
* server populates; returns {@code -1} when unavailable.
|
||||
*/
|
||||
public class NetworkModuleFabric implements NetworkModule {
|
||||
|
||||
private final MinecraftClient mc;
|
||||
|
||||
public NetworkModuleFabric(MinecraftClient mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNetwork() {
|
||||
return mc.getNetworkHandler() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
if (mc.player == null || mc.getNetworkHandler() == null)
|
||||
return -1;
|
||||
|
||||
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(mc.player.getUuid());
|
||||
|
||||
return entry != null ? entry.getLatency() : -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.saturnclient.common.module.PlayerModule;
|
||||
import org.saturnclient.common.ref.asset.SpriteRef;
|
||||
import org.saturnclient.common.ref.game.ItemStackRef;
|
||||
import org.saturnclient.feature.features.StatusEffectsFeature;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link PlayerModule}.
|
||||
*
|
||||
* All access to {@link MinecraftClient} and {@link ClientPlayerEntity}
|
||||
* is encapsulated here. Features only ever see the {@link PlayerModule}
|
||||
* interface; they never import Minecraft classes directly.
|
||||
*
|
||||
* Every method guards against a missing player and returns a safe
|
||||
* default so features are free of null checks.
|
||||
*/
|
||||
public class PlayerModuleFabric implements PlayerModule {
|
||||
|
||||
private final MinecraftClient mc;
|
||||
|
||||
public PlayerModuleFabric(MinecraftClient mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Internal helper — never returns null
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/** Returns the local player, or {@code null} if one is not loaded. */
|
||||
private ClientPlayerEntity player() {
|
||||
return mc.player;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Presence
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean hasPlayer() {
|
||||
return mc.player != null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Position
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
return hasPlayer() ? (int) player().getX() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return hasPlayer() ? (int) player().getY() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ() {
|
||||
return hasPlayer() ? (int) player().getZ() : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Input / movement
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean isForwardPressed() {
|
||||
return mc.options.forwardKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackPressed() {
|
||||
return mc.options.backKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeftPressed() {
|
||||
return mc.options.leftKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRightPressed() {
|
||||
return mc.options.rightKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJumpPressed() {
|
||||
return mc.options.jumpKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttackPressed() {
|
||||
return mc.options.attackKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsePressed() {
|
||||
return mc.options.useKey.isPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSneaking() {
|
||||
return hasPlayer() && player().isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem() {
|
||||
return hasPlayer() && player().isUsingItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHorizontalCollision() {
|
||||
return hasPlayer() && player().horizontalCollision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnGround() {
|
||||
return hasPlayer() && player().isOnGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSprinting(boolean sprinting) {
|
||||
if (hasPlayer())
|
||||
player().setSprinting(sprinting);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Velocity
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Velocity getVelocity() {
|
||||
if (!hasPlayer())
|
||||
return Velocity.ZERO;
|
||||
var v = player().getVelocity();
|
||||
return new Velocity(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Stats
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public float getHealth() {
|
||||
return hasPlayer() ? player().getHealth() : 0f;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Equipment
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ItemStackRef getMainHand() {
|
||||
return wrap(hasPlayer() ? player().getMainHandStack() : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getHelmet() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(3) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getChestplate() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(2) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getLeggings() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(1) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getBoots() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(0) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Dummy equipment
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ItemStackRef getDummyMainHand() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(3) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getDummyHelmet() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(3) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getDummyChestplate() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(2) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getDummyLeggings() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(1) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStackRef getDummyBoots() {
|
||||
return wrap(hasPlayer() ? player().getInventory().armor.get(0) : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Status effects
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public List<? extends StatusEffectsFeature.EffectView> getActiveEffects() {
|
||||
if (!hasPlayer())
|
||||
return Collections.emptyList();
|
||||
|
||||
var effects = player().getActiveStatusEffects().values();
|
||||
List<FabricEffectView> result = new ArrayList<>(effects.size());
|
||||
for (StatusEffectInstance instance : effects) {
|
||||
result.add(new FabricEffectView(instance));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends StatusEffectsFeature.EffectView> getDummyEffects() {
|
||||
if (!hasPlayer())
|
||||
return Collections.emptyList();
|
||||
|
||||
var effects = player().getActiveStatusEffects().values();
|
||||
List<FabricEffectView> result = new ArrayList<>(effects.size());
|
||||
for (StatusEffectInstance instance : effects) {
|
||||
result.add(new FabricEffectView(instance));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
private static ItemStackRef wrap(ItemStack stack) {
|
||||
return (ItemStackRef) (Object) stack;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Inner — EffectView adapter
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Bridges a Fabric {@link StatusEffectInstance} to the
|
||||
* platform-neutral {@link StatusEffectsFeature.EffectView}.
|
||||
*/
|
||||
public static final class FabricEffectView implements StatusEffectsFeature.EffectView {
|
||||
|
||||
private final StatusEffectInstance instance;
|
||||
|
||||
public FabricEffectView(StatusEffectInstance instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowIcon() {
|
||||
return instance.shouldShowIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Minecraft sprite identifier for this effect's icon.
|
||||
* The platform-specific renderer knows how to draw a
|
||||
* {@link net.minecraft.util.Identifier}.
|
||||
*/
|
||||
@Override
|
||||
public SpriteRef getIcon() {
|
||||
return (SpriteRef) SaturnClient.client.getStatusEffectSpriteManager().getSprite(instance.getEffectType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfinite() {
|
||||
return instance.isInfinite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInfiniteText() {
|
||||
return "∞";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDurationSeconds() {
|
||||
return instance.getDuration() / 20; // ticks → seconds
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.Perspective;
|
||||
|
||||
import org.saturnclient.common.module.RenderModule;
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link RenderModule}.
|
||||
*
|
||||
* Window dimensions and FPS are read directly from
|
||||
* {@link MinecraftClient}. Camera perspective control delegates to
|
||||
* {@link net.minecraft.client.option.GameOptions}.
|
||||
*/
|
||||
public class RenderModuleFabric implements RenderModule {
|
||||
|
||||
private final MinecraftClient mc;
|
||||
|
||||
public RenderModuleFabric(MinecraftClient mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Window
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public int getScaledWindowWidth() {
|
||||
return mc.getWindow().getScaledWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScaledWindowHeight() {
|
||||
return mc.getWindow().getScaledHeight();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Performance
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public int getFps() {
|
||||
return mc.getCurrentFps();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Camera perspective
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean isFirstPerson() {
|
||||
return mc.options.getPerspective() == Perspective.FIRST_PERSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirstPerson() {
|
||||
mc.options.setPerspective(Perspective.FIRST_PERSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThirdPersonBack() {
|
||||
mc.options.setPerspective(Perspective.THIRD_PERSON_BACK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.saturnclient.impl.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
import org.saturnclient.common.module.WorldModule;
|
||||
|
||||
/**
|
||||
* Fabric implementation of {@link WorldModule}.
|
||||
*
|
||||
* Reads the client world's time directly from {@link MinecraftClient}.
|
||||
* Returns safe defaults when no world is loaded.
|
||||
*/
|
||||
public class WorldModuleFabric implements WorldModule {
|
||||
|
||||
private final MinecraftClient mc;
|
||||
|
||||
public WorldModuleFabric(MinecraftClient mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWorld() {
|
||||
return mc.world != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total world age in ticks, or {@code 0} when no world
|
||||
* is loaded. Used by {@link TpsFeature#onTimePacket} (which is
|
||||
* called from the mixin and stores its own copy) and by
|
||||
* {@link DayCounterFeature} for the day count.
|
||||
*/
|
||||
@Override
|
||||
public long getWorldAge() {
|
||||
return hasWorld() ? mc.world.getTime() : 0L;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user