Refactor features to use module system
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
package org.saturnclient.common.module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EntityModule provides queries about entities in the current world.
|
||||||
|
*
|
||||||
|
* Designed to be thin so that feature logic is not polluted with
|
||||||
|
* engine-specific entity types.
|
||||||
|
*/
|
||||||
|
public interface EntityModule {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Crosshair / targeting
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns true when the player's crosshair is aimed at a living entity. */
|
||||||
|
boolean isTargetingLivingEntity();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Nametag entity state
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the minimal observable state of an entity needed for
|
||||||
|
* nametag rendering. Implementations are created per-entity each
|
||||||
|
* frame; they are intentionally not cached.
|
||||||
|
*/
|
||||||
|
interface EntityState {
|
||||||
|
|
||||||
|
/** The entity's display name, or {@code null} if it has none. */
|
||||||
|
String getCustomName();
|
||||||
|
|
||||||
|
float getHealth();
|
||||||
|
float getMaxHealth();
|
||||||
|
EntityType getEntityType();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EntityType {
|
||||||
|
PLAYER,
|
||||||
|
HOSTILE,
|
||||||
|
PASSIVE,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.saturnclient.common.module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NetworkModule provides access to the current server connection.
|
||||||
|
*
|
||||||
|
* Methods return safe defaults (false / -1) when not connected.
|
||||||
|
*/
|
||||||
|
public interface NetworkModule {
|
||||||
|
|
||||||
|
/** Returns true when a server connection is active. */
|
||||||
|
boolean hasNetwork();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current ping to the server in milliseconds.
|
||||||
|
* Returns {@code -1} when unavailable.
|
||||||
|
*/
|
||||||
|
int getPing();
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package org.saturnclient.common.module;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.saturnclient.common.ref.game.ItemStackRef;
|
||||||
|
import org.saturnclient.feature.features.StatusEffectsFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PlayerModule provides access to the state of the local player.
|
||||||
|
*
|
||||||
|
* All methods return safe defaults (false / 0 / empty) when no player
|
||||||
|
* is present so that features do not need to null-check everywhere.
|
||||||
|
*/
|
||||||
|
public interface PlayerModule {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Presence
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns true when a local player entity exists. */
|
||||||
|
boolean hasPlayer();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Position
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
int getX();
|
||||||
|
|
||||||
|
int getY();
|
||||||
|
|
||||||
|
int getZ();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Movement / input
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
boolean isForwardPressed();
|
||||||
|
|
||||||
|
boolean isBackPressed();
|
||||||
|
|
||||||
|
boolean isLeftPressed();
|
||||||
|
|
||||||
|
boolean isRightPressed();
|
||||||
|
|
||||||
|
boolean isJumpPressed();
|
||||||
|
|
||||||
|
boolean isAttackPressed();
|
||||||
|
|
||||||
|
boolean isUsePressed();
|
||||||
|
|
||||||
|
boolean isSneaking();
|
||||||
|
|
||||||
|
boolean isUsingItem();
|
||||||
|
|
||||||
|
boolean hasHorizontalCollision();
|
||||||
|
|
||||||
|
boolean isOnGround();
|
||||||
|
|
||||||
|
void setSprinting(boolean sprinting);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Velocity
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
Velocity getVelocity();
|
||||||
|
|
||||||
|
/** Immutable snapshot of the player's velocity components. */
|
||||||
|
final class Velocity {
|
||||||
|
public final double x;
|
||||||
|
public final double y;
|
||||||
|
public final double z;
|
||||||
|
|
||||||
|
public Velocity(double x, double y, double z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Stats
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
float getHealth();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Effects / Potions
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of the player’s currently active effects.
|
||||||
|
* Safe default: empty list if no player is present.
|
||||||
|
*/
|
||||||
|
List<StatusEffectsFeature.EffectView> getActiveEffects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of dummy effects used in HUD editor / preview mode.
|
||||||
|
* Safe default: empty list.
|
||||||
|
*/
|
||||||
|
List<StatusEffectsFeature.EffectView> getDummyEffects();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Inventory / equipment
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
ItemStackRef getMainHand();
|
||||||
|
|
||||||
|
ItemStackRef getHelmet();
|
||||||
|
|
||||||
|
ItemStackRef getChestplate();
|
||||||
|
|
||||||
|
ItemStackRef getLeggings();
|
||||||
|
|
||||||
|
ItemStackRef getBoots();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Dummy / preview data (used in HUD editor)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
ItemStackRef getDummyMainHand();
|
||||||
|
|
||||||
|
ItemStackRef getDummyHelmet();
|
||||||
|
|
||||||
|
ItemStackRef getDummyChestplate();
|
||||||
|
|
||||||
|
ItemStackRef getDummyLeggings();
|
||||||
|
|
||||||
|
ItemStackRef getDummyBoots();
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.saturnclient.common.module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RenderModule provides access to render-time information that does
|
||||||
|
* not belong to the player or world, such as the current window
|
||||||
|
* dimensions, camera perspective, and the frame's FPS counter.
|
||||||
|
*/
|
||||||
|
public interface RenderModule {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Window
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
int getScaledWindowWidth();
|
||||||
|
int getScaledWindowHeight();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Performance
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
int getFps();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Camera perspective
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
boolean isFirstPerson();
|
||||||
|
|
||||||
|
void setFirstPerson();
|
||||||
|
void setThirdPersonBack();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.saturnclient.common.module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorldModule provides access to world-level information that is not
|
||||||
|
* tied to the local player (time of day, total age, etc.).
|
||||||
|
*
|
||||||
|
* All methods return safe defaults when no world is loaded.
|
||||||
|
*/
|
||||||
|
public interface WorldModule {
|
||||||
|
|
||||||
|
/** Returns true when a world is currently loaded. */
|
||||||
|
boolean hasWorld();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total world age in ticks.
|
||||||
|
* Used by {@link org.saturnclient.feature.TpsFeature} to compute TPS.
|
||||||
|
*/
|
||||||
|
long getWorldAge();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of in-game days that have passed.
|
||||||
|
* Computed as {@code worldAge / 24000}.
|
||||||
|
*/
|
||||||
|
default long getDay() {
|
||||||
|
return getWorldAge() / 24_000L;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,35 @@
|
|||||||
package org.saturnclient.common.provider;
|
package org.saturnclient.common.provider;
|
||||||
|
|
||||||
|
import org.saturnclient.common.module.EntityModule;
|
||||||
|
import org.saturnclient.common.module.NetworkModule;
|
||||||
|
import org.saturnclient.common.module.PlayerModule;
|
||||||
|
import org.saturnclient.common.module.RenderModule;
|
||||||
|
import org.saturnclient.common.module.WorldModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModuleProvider is the single entry point for features to access
|
||||||
|
* all common engine abstractions. Implementations are supplied by
|
||||||
|
* the platform-specific client bootstrap (e.g. the Fabric/Forge mod).
|
||||||
|
*
|
||||||
|
* Features should receive a ModuleProvider in their constructor and
|
||||||
|
* store it; they must NOT cache individual sub-modules across ticks
|
||||||
|
* because the provider may swap implementations at runtime (e.g. on
|
||||||
|
* world change).
|
||||||
|
*/
|
||||||
public interface ModuleProvider {
|
public interface ModuleProvider {
|
||||||
|
|
||||||
|
/** Access to the local player and movement state. */
|
||||||
|
PlayerModule player();
|
||||||
|
|
||||||
|
/** Access to the game world (time, TPS packets, etc.). */
|
||||||
|
WorldModule world();
|
||||||
|
|
||||||
|
/** Access to entity / nametag queries. */
|
||||||
|
EntityModule entity();
|
||||||
|
|
||||||
|
/** Access to rendering utilities (window size, textures, etc.). */
|
||||||
|
RenderModule render();
|
||||||
|
|
||||||
|
/** Access to the network / server connection. */
|
||||||
|
NetworkModule network();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package org.saturnclient.feature;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.saturnclient.feature.features.*;
|
||||||
|
|
||||||
public class FeatureManager {
|
public class FeatureManager {
|
||||||
public static List<Feature> ENABLED_MODS = new ArrayList<>();
|
public static List<Feature> ENABLED_MODS = new ArrayList<>();
|
||||||
public static Feature[] MODS = {};
|
public static Feature[] MODS = {};
|
||||||
|
|
||||||
public static void init(ModuleProvider provider) {
|
public static void init() {
|
||||||
MODS = provider.getMods();
|
|
||||||
updateEnabledModules();
|
updateEnabledModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,12 +21,4 @@ public class FeatureManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Feature[] getAllMods() {
|
|
||||||
return MODS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ModuleProvider {
|
|
||||||
Feature[] getMods();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.common.ref.game.ItemStackRef;
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.ArmorDisplayInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
|
|
||||||
public class ArmorDisplay extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(40, 75);
|
|
||||||
private static Property<Boolean> useMainHand = Property.bool(true);
|
|
||||||
|
|
||||||
private final ArmorDisplayInterface minecraft;
|
|
||||||
|
|
||||||
public ArmorDisplay(ArmorDisplayInterface minecraft) {
|
|
||||||
super(new FeatureDetails("Armor Display", "armor")
|
|
||||||
.description("Displays armor durability")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
useMainHand.named("Use Main Hand"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderArmor(RenderScope scope,
|
|
||||||
ItemStackRef mainHand,
|
|
||||||
ItemStackRef helmet,
|
|
||||||
ItemStackRef chestplate,
|
|
||||||
ItemStackRef leggings,
|
|
||||||
ItemStackRef boots) {
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
if (useMainHand.value && !mainHand.isEmpty()) {
|
|
||||||
scope.drawItem(mainHand, 0, 15 * row);
|
|
||||||
renderHealth(scope, row, mainHand.getMaxDamage(), mainHand.getDamage());
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!helmet.isEmpty()) {
|
|
||||||
scope.drawItem(helmet, 0, 15 * row);
|
|
||||||
renderHealth(scope, row, helmet.getMaxDamage(), helmet.getDamage());
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chestplate.isEmpty()) {
|
|
||||||
scope.drawItem(chestplate, 0, 15 * row);
|
|
||||||
renderHealth(scope, row, chestplate.getMaxDamage(), chestplate.getDamage());
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!leggings.isEmpty()) {
|
|
||||||
scope.drawItem(leggings, 0, 15 * row);
|
|
||||||
renderHealth(scope, row, leggings.getMaxDamage(), leggings.getDamage());
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!boots.isEmpty()) {
|
|
||||||
scope.drawItem(boots, 0, 15 * row);
|
|
||||||
renderHealth(scope, row, boots.getMaxDamage(), boots.getDamage());
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderHealth(RenderScope scope, int i, int maxDamage, int damage) {
|
|
||||||
if (maxDamage > 0) {
|
|
||||||
scope.drawText(
|
|
||||||
0.5f,
|
|
||||||
"" + (maxDamage - damage),
|
|
||||||
17,
|
|
||||||
(15 * i) + 3,
|
|
||||||
dimensions.font.value,
|
|
||||||
dimensions.fgColor.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
renderArmor(scope,
|
|
||||||
minecraft.getMainHand(),
|
|
||||||
minecraft.getHelmet(),
|
|
||||||
minecraft.getChestplate(),
|
|
||||||
minecraft.getLeggings(),
|
|
||||||
minecraft.getBoots());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderArmor(scope,
|
|
||||||
minecraft.getDummyMainHand(),
|
|
||||||
minecraft.getDummyHelmet(),
|
|
||||||
minecraft.getDummyChestplate(),
|
|
||||||
minecraft.getDummyLeggings(),
|
|
||||||
minecraft.getDummyBoots());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.PlayerModule;
|
||||||
|
import org.saturnclient.common.ref.game.ItemStackRef;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ArmorDisplayFeature renders the durability of the player's equipped
|
||||||
|
* armor pieces (and optionally the main-hand item) as a HUD overlay.
|
||||||
|
*
|
||||||
|
* Engine access is provided exclusively through {@link ModuleProvider}.
|
||||||
|
*/
|
||||||
|
public class ArmorDisplayFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> useMainHand = Property.bool(true);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(40, 75);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public ArmorDisplayFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Armor Display", "armor")
|
||||||
|
.description("Displays armor durability")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
useMainHand.named("Use Main Hand"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
PlayerModule player = modules.player();
|
||||||
|
renderArmor(scope,
|
||||||
|
player.getMainHand(),
|
||||||
|
player.getHelmet(),
|
||||||
|
player.getChestplate(),
|
||||||
|
player.getLeggings(),
|
||||||
|
player.getBoots());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
PlayerModule player = modules.player();
|
||||||
|
renderArmor(scope,
|
||||||
|
player.getDummyMainHand(),
|
||||||
|
player.getDummyHelmet(),
|
||||||
|
player.getDummyChestplate(),
|
||||||
|
player.getDummyLeggings(),
|
||||||
|
player.getDummyBoots());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering helpers
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderArmor(RenderScope scope,
|
||||||
|
ItemStackRef mainHand,
|
||||||
|
ItemStackRef helmet,
|
||||||
|
ItemStackRef chestplate,
|
||||||
|
ItemStackRef leggings,
|
||||||
|
ItemStackRef boots) {
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
if (useMainHand.value && !mainHand.isEmpty()) {
|
||||||
|
row = renderSlot(scope, mainHand, row);
|
||||||
|
}
|
||||||
|
if (!helmet.isEmpty())
|
||||||
|
row = renderSlot(scope, helmet, row);
|
||||||
|
if (!chestplate.isEmpty())
|
||||||
|
row = renderSlot(scope, chestplate, row);
|
||||||
|
if (!leggings.isEmpty())
|
||||||
|
row = renderSlot(scope, leggings, row);
|
||||||
|
if (!boots.isEmpty())
|
||||||
|
row = renderSlot(scope, boots, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a single equipment slot and returns the next row index.
|
||||||
|
*/
|
||||||
|
private int renderSlot(RenderScope scope, ItemStackRef item, int row) {
|
||||||
|
scope.drawItem(item, 0, 15 * row);
|
||||||
|
|
||||||
|
if (item.getMaxDamage() > 0) {
|
||||||
|
int remaining = item.getMaxDamage() - item.getDamage();
|
||||||
|
scope.drawText(0.5f, String.valueOf(remaining),
|
||||||
|
17, (15 * row) + 3,
|
||||||
|
layout.font.value, layout.fgColor.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return row + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.AutoSprintInterface;
|
|
||||||
|
|
||||||
public class AutoSprint extends Feature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
|
|
||||||
private final AutoSprintInterface minecraft;
|
|
||||||
|
|
||||||
public AutoSprint(AutoSprintInterface minecraft) {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("Auto Sprint", "sprint")
|
|
||||||
.description("Makes the player always sprint")
|
|
||||||
.tags("Movement")
|
|
||||||
.version("v0.1.0"),
|
|
||||||
enabled.named("Enabled"));
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
if (!minecraft.hasPlayer() || !minecraft.hasNetwork()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minecraft.isForwardPressed()
|
|
||||||
&& !minecraft.isBackPressed()
|
|
||||||
&& !minecraft.isSneaking()
|
|
||||||
&& !minecraft.hasHorizontalCollision()
|
|
||||||
&& !minecraft.isUsingItem()) {
|
|
||||||
|
|
||||||
minecraft.setSprinting(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.NetworkModule;
|
||||||
|
import org.saturnclient.common.module.PlayerModule;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AutoSprintFeature automatically enables sprinting whenever the
|
||||||
|
* player moves forward and no sprint-blocking condition is active.
|
||||||
|
*/
|
||||||
|
public class AutoSprintFeature extends Feature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public AutoSprintFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Auto Sprint", "sprint")
|
||||||
|
.description("Makes the player always sprint")
|
||||||
|
.tags("Movement")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"));
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
PlayerModule player = modules.player();
|
||||||
|
NetworkModule network = modules.network();
|
||||||
|
|
||||||
|
if (!player.hasPlayer() || !network.hasNetwork()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.isForwardPressed()
|
||||||
|
&& !player.isBackPressed()
|
||||||
|
&& !player.isSneaking()
|
||||||
|
&& !player.hasHorizontalCollision()
|
||||||
|
&& !player.isUsingItem()) {
|
||||||
|
|
||||||
|
player.setSprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Clock extends Feature implements HudFeature {
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Integer> format = Property.select(0, "24 hour", "12 hour");
|
|
||||||
private static Property<Boolean> seconds = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(60, Fonts.getHeight());
|
|
||||||
|
|
||||||
public Clock() {
|
|
||||||
super(new FeatureDetails("Clock", "clock")
|
|
||||||
.description("Displays the current real-world time")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
format.named("Format"),
|
|
||||||
seconds.named("Show seconds"),
|
|
||||||
dimensions.prop());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderClock(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
renderClock(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderClock(RenderScope scope) {
|
|
||||||
String text = "";
|
|
||||||
LocalTime currentTime = LocalTime.now();
|
|
||||||
|
|
||||||
switch (format.value) {
|
|
||||||
case 0:
|
|
||||||
if (seconds.value) {
|
|
||||||
text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
|
|
||||||
} else {
|
|
||||||
text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (seconds.value) {
|
|
||||||
text = currentTime.format(DateTimeFormatter.ofPattern("hh:mm:ss a"));
|
|
||||||
} else {
|
|
||||||
text = currentTime.format(DateTimeFormatter.ofPattern("hh:mm a"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.drawText(text,
|
|
||||||
0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
dimensions.height = 18 * text.split("\n").length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClockFeature displays the real-world time as a HUD element.
|
||||||
|
* No engine access is required; {@link LocalTime} is used directly.
|
||||||
|
*/
|
||||||
|
public class ClockFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Integer> format = Property.select(0, "24 hour", "12 hour");
|
||||||
|
private static final Property<Boolean> showSeconds = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(60, Fonts.getHeight());
|
||||||
|
|
||||||
|
public ClockFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Clock", "clock")
|
||||||
|
.description("Displays the current real-world time")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
format.named("Format"),
|
||||||
|
showSeconds.named("Show seconds"),
|
||||||
|
layout.prop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderClock(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderClock(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderClock(RenderScope scope) {
|
||||||
|
String text = buildTimeString(LocalTime.now());
|
||||||
|
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
layout.height = 18 * text.split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildTimeString(LocalTime time) {
|
||||||
|
return switch (format.value) {
|
||||||
|
case 1 -> time.format(showSeconds.value
|
||||||
|
? DateTimeFormatter.ofPattern("hh:mm:ss a")
|
||||||
|
: DateTimeFormatter.ofPattern("hh:mm a"));
|
||||||
|
default -> time.format(showSeconds.value
|
||||||
|
? DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||||
|
: DateTimeFormatter.ofPattern("HH:mm"));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.CoordinatesInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Coordinates extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
|
|
||||||
private static Property<Integer> displayMethod = Property.select(
|
|
||||||
0,
|
|
||||||
"Flat",
|
|
||||||
"Flat annotated",
|
|
||||||
"Newline",
|
|
||||||
"Newline annotated");
|
|
||||||
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(40, 18);
|
|
||||||
|
|
||||||
private final CoordinatesInterface minecraft;
|
|
||||||
|
|
||||||
public Coordinates(CoordinatesInterface minecraft) {
|
|
||||||
super(new FeatureDetails("Coordinates", "coords")
|
|
||||||
.description("Displays your current coordinates")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
displayMethod.named("Display Method"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderCoords(532, 69, 253, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
int x = minecraft.getPlayerX();
|
|
||||||
int y = minecraft.getPlayerY();
|
|
||||||
int z = minecraft.getPlayerZ();
|
|
||||||
|
|
||||||
renderCoords(x, y, z, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderCoords(int x, int y, int z, RenderScope scope) {
|
|
||||||
|
|
||||||
String f = "%d %d %d";
|
|
||||||
|
|
||||||
switch (displayMethod.value) {
|
|
||||||
case 1:
|
|
||||||
f = "X: %d Y: %d Z: %d";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
f = "%d\n%d\n%d";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
f = "X: %d\nY: %d\nZ: %d";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String text = String.format(f, x, y, z);
|
|
||||||
|
|
||||||
scope.drawText(
|
|
||||||
text,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
dimensions.font.value,
|
|
||||||
dimensions.fgColor.value);
|
|
||||||
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
dimensions.height = 18 * text.split("\n").length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CoordinatesFeature displays the player's current XYZ position as a
|
||||||
|
* HUD element with multiple configurable display formats.
|
||||||
|
*/
|
||||||
|
public class CoordinatesFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Integer> displayMethod = Property.select(0,
|
||||||
|
"Flat",
|
||||||
|
"Flat annotated",
|
||||||
|
"Newline",
|
||||||
|
"Newline annotated");
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(40, 18);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public CoordinatesFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Coordinates", "coords")
|
||||||
|
.description("Displays your current coordinates")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
displayMethod.named("Display Method"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderCoords(
|
||||||
|
modules.player().getX(),
|
||||||
|
modules.player().getY(),
|
||||||
|
modules.player().getZ(),
|
||||||
|
scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderCoords(532, 69, 253, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderCoords(int x, int y, int z, RenderScope scope) {
|
||||||
|
String fmt = switch (displayMethod.value) {
|
||||||
|
case 1 -> "X: %d Y: %d Z: %d";
|
||||||
|
case 2 -> "%d\n%d\n%d";
|
||||||
|
case 3 -> "X: %d\nY: %d\nZ: %d";
|
||||||
|
default -> "%d %d %d";
|
||||||
|
};
|
||||||
|
|
||||||
|
String text = String.format(fmt, x, y, z);
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
layout.height = 18 * text.split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.CrosshairInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Textures;
|
|
||||||
|
|
||||||
public class Crosshair extends Feature {
|
|
||||||
|
|
||||||
public static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
|
|
||||||
private final CrosshairInterface minecraft;
|
|
||||||
|
|
||||||
public Crosshair(CrosshairInterface minecraft) {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("Crosshair", "crosshair")
|
|
||||||
.description("Changes the crosshair if you're aiming at a entity")
|
|
||||||
.tags("Visuals", "Utility")
|
|
||||||
.version("v0.1.0"),
|
|
||||||
enabled.named("Enabled"));
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(RenderScope scope) {
|
|
||||||
|
|
||||||
if (enabled.value && minecraft.isTargetingLivingEntity()) {
|
|
||||||
|
|
||||||
int scaledWidth = 15;
|
|
||||||
int scaledHeight = 15;
|
|
||||||
|
|
||||||
scope.drawTexture(
|
|
||||||
Textures.CROSSHAIR_RANGE,
|
|
||||||
(scope.getScaledWindowWidth() - scaledWidth) / 2,
|
|
||||||
(scope.getScaledWindowHeight() - scaledHeight) / 2,
|
|
||||||
0.0F,
|
|
||||||
0.0F,
|
|
||||||
scaledWidth,
|
|
||||||
scaledHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.RenderModule;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Textures;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CrosshairFeature renders an alternative crosshair texture when the
|
||||||
|
* player is aiming at a living entity.
|
||||||
|
*/
|
||||||
|
public class CrosshairFeature extends Feature {
|
||||||
|
|
||||||
|
public static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
|
||||||
|
private static final int CROSSHAIR_SIZE = 15;
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public CrosshairFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Crosshair", "crosshair")
|
||||||
|
.description("Changes the crosshair when aiming at an entity")
|
||||||
|
.tags("Visuals", "Utility")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"));
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RenderScope scope) {
|
||||||
|
if (!enabled.value || !modules.entity().isTargetingLivingEntity()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderModule render = modules.render();
|
||||||
|
int x = (render.getScaledWindowWidth() - CROSSHAIR_SIZE) / 2;
|
||||||
|
int y = (render.getScaledWindowHeight() - CROSSHAIR_SIZE) / 2;
|
||||||
|
|
||||||
|
scope.drawTexture(
|
||||||
|
Textures.CROSSHAIR_RANGE,
|
||||||
|
x, y,
|
||||||
|
0.0F, 0.0F,
|
||||||
|
CROSSHAIR_SIZE, CROSSHAIR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.DayCounterInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class DayCounter extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(40, 18);
|
|
||||||
|
|
||||||
private final DayCounterInterface minecraft;
|
|
||||||
|
|
||||||
long day = 0;
|
|
||||||
|
|
||||||
public DayCounter(DayCounterInterface minecraft) {
|
|
||||||
super(new FeatureDetails("DayCounter", "day")
|
|
||||||
.description("Displays the number of days that have passed")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderDay(271, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
day = minecraft.getDay();
|
|
||||||
renderDay(day, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderDay(long day, RenderScope scope) {
|
|
||||||
String text = "Day: " + day;
|
|
||||||
|
|
||||||
scope.drawText(
|
|
||||||
text,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
dimensions.font.value,
|
|
||||||
dimensions.fgColor.value);
|
|
||||||
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
dimensions.height = 18 * text.split("\n").length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DayCounterFeature shows how many in-game days have elapsed.
|
||||||
|
* Day count is derived from the world age via {@link WorldModule}.
|
||||||
|
*/
|
||||||
|
public class DayCounterFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(40, 18);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public DayCounterFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Day Counter", "day")
|
||||||
|
.description("Displays the number of in-game days elapsed")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderDay(modules.world().getDay(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderDay(271L, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderDay(long day, RenderScope scope) {
|
||||||
|
String text = "Day: " + day;
|
||||||
|
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
layout.height = 18 * text.split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.FpsInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Fps extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(60, Fonts.getHeight());
|
|
||||||
|
|
||||||
private final FpsInterface minecraft;
|
|
||||||
|
|
||||||
public Fps(FpsInterface minecraft) {
|
|
||||||
super(new FeatureDetails("FPS Display", "fps")
|
|
||||||
.description("Displays current FPS")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
int fps = minecraft.getFps();
|
|
||||||
|
|
||||||
String text = fps + " FPS";
|
|
||||||
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
String text = "369 FPS";
|
|
||||||
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FpsFeature displays the current frames-per-second as a HUD element.
|
||||||
|
* FPS is obtained from {@link RenderModule} rather than cached in the feature.
|
||||||
|
*/
|
||||||
|
public class FpsFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(60, Fonts.getHeight());
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public FpsFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("FPS Display", "fps")
|
||||||
|
.description("Displays current FPS")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderFps(modules.render().getFps(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderFps(369, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderFps(int fps, RenderScope scope) {
|
||||||
|
String text = fps + " FPS";
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.common.provider.GLFWProvider;
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.FreelookInterface;
|
|
||||||
|
|
||||||
public class Freelook extends Feature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Boolean> toggle = Property.bool(true);
|
|
||||||
|
|
||||||
public static Property<Integer> freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H);
|
|
||||||
|
|
||||||
public static boolean isFreeLooking = false;
|
|
||||||
private static boolean was1stPerson = false;
|
|
||||||
|
|
||||||
private final FreelookInterface minecraft;
|
|
||||||
|
|
||||||
public Freelook(FreelookInterface minecraft) {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("Freelook", "freelook")
|
|
||||||
.description("Allows you to look around freely without moving your character")
|
|
||||||
.tags("Movement", "Camera")
|
|
||||||
.version("0.1.0"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
toggle.named("Toggle freelook"),
|
|
||||||
freelookKey.named("Freelook Keybinding"));
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
|
|
||||||
if (toggle.value) {
|
|
||||||
|
|
||||||
if (freelookKey.wasKeyPressed()) {
|
|
||||||
|
|
||||||
if (isFreeLooking) {
|
|
||||||
stopFreelook();
|
|
||||||
} else {
|
|
||||||
startFreelook();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (freelookKey.isKeyPressed()) {
|
|
||||||
|
|
||||||
if (!isFreeLooking) {
|
|
||||||
startFreelook();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (isFreeLooking) {
|
|
||||||
|
|
||||||
stopFreelook();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startFreelook() {
|
|
||||||
isFreeLooking = true;
|
|
||||||
|
|
||||||
was1stPerson = minecraft.isFirstPerson();
|
|
||||||
|
|
||||||
minecraft.setThirdPersonBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopFreelook() {
|
|
||||||
|
|
||||||
if (was1stPerson) {
|
|
||||||
minecraft.setFirstPerson();
|
|
||||||
}
|
|
||||||
|
|
||||||
isFreeLooking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.RenderModule;
|
||||||
|
import org.saturnclient.common.provider.GLFWProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FreelookFeature lets the player look around freely (in third-person)
|
||||||
|
* without changing the direction their character walks.
|
||||||
|
*
|
||||||
|
* Camera perspective is controlled through {@link RenderModule} so
|
||||||
|
* that the feature itself contains no direct Minecraft API calls.
|
||||||
|
*/
|
||||||
|
public class FreelookFeature extends Feature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> toggle = Property.bool(true);
|
||||||
|
public static final Property<Integer> freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H);
|
||||||
|
|
||||||
|
/** Whether freelook is currently active (read by the camera mixin). */
|
||||||
|
public static boolean isFreeLooking = false;
|
||||||
|
private static boolean wasFirstPerson = false;
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public FreelookFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Freelook", "freelook")
|
||||||
|
.description("Look around freely without moving your character")
|
||||||
|
.tags("Movement", "Camera")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
toggle.named("Toggle freelook"),
|
||||||
|
freelookKey.named("Freelook Keybinding"));
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (toggle.value) {
|
||||||
|
if (freelookKey.wasKeyPressed()) {
|
||||||
|
if (isFreeLooking)
|
||||||
|
stopFreelook();
|
||||||
|
else
|
||||||
|
startFreelook();
|
||||||
|
}
|
||||||
|
} else if (freelookKey.isKeyPressed()) {
|
||||||
|
if (!isFreeLooking)
|
||||||
|
startFreelook();
|
||||||
|
} else if (isFreeLooking) {
|
||||||
|
stopFreelook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
if (!e && isFreeLooking)
|
||||||
|
stopFreelook();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void startFreelook() {
|
||||||
|
RenderModule render = modules.render();
|
||||||
|
wasFirstPerson = render.isFirstPerson();
|
||||||
|
render.setThirdPersonBack();
|
||||||
|
isFreeLooking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopFreelook() {
|
||||||
|
if (wasFirstPerson) {
|
||||||
|
modules.render().setFirstPerson();
|
||||||
|
}
|
||||||
|
isFreeLooking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
|
|
||||||
public class Fullbright extends Feature {
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Integer> brightness = Property.integer(100);
|
|
||||||
|
|
||||||
public Fullbright() {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("Fullbright", "fullbright")
|
|
||||||
.description("Allows you to see in the dark")
|
|
||||||
.tags("Camera")
|
|
||||||
.version("v0.1.0"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
brightness.named("Brightness %"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean shouldOverrideBrightness() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float getBrightnessValue() {
|
|
||||||
return brightness.value / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FullbrightFeature overrides the game's gamma/brightness to let the
|
||||||
|
* player see in complete darkness.
|
||||||
|
*
|
||||||
|
* Actual brightness injection happens in a mixin; this class owns only
|
||||||
|
* the configuration and the two static query methods the mixin calls.
|
||||||
|
*
|
||||||
|
* No ModuleProvider is needed — this feature has no per-tick engine
|
||||||
|
* interaction; the mixin polls the static helpers directly.
|
||||||
|
*/
|
||||||
|
public class FullbrightFeature extends Feature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Integer> brightness = Property.integer(100);
|
||||||
|
|
||||||
|
public FullbrightFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Fullbright", "fullbright")
|
||||||
|
.description("Allows you to see in the dark")
|
||||||
|
.tags("Camera")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
brightness.named("Brightness %"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Mixin query API (static so the mixin can call without an instance)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns {@code true} when the brightness override should be applied. */
|
||||||
|
public static boolean shouldOverrideBrightness() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the brightness multiplier to apply.
|
||||||
|
* The property stores a percentage (0–100+); the mixin expects a
|
||||||
|
* normalised float where 1.0 ≈ 100 %.
|
||||||
|
*/
|
||||||
|
public static float getBrightnessValue() {
|
||||||
|
return brightness.value / 10.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.HealthDisplayInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class HealthDisplay extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Integer> displayMode = Property.select(0, "Health", "Hearts");
|
|
||||||
private static Property<Integer> decimals = Property.select(0, "0", "1", "2");
|
|
||||||
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(40, 18);
|
|
||||||
|
|
||||||
private final HealthDisplayInterface minecraft;
|
|
||||||
|
|
||||||
public HealthDisplay(HealthDisplayInterface minecraft) {
|
|
||||||
super(new FeatureDetails("Health display", "health")
|
|
||||||
.description("Displays your current health")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
displayMode.named("Display mode"),
|
|
||||||
decimals.named("Decimals"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderHealth(10, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
float health = minecraft.getPlayerHealth();
|
|
||||||
|
|
||||||
switch (displayMode.value) {
|
|
||||||
case 1:
|
|
||||||
health = health / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHealth(health, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderHealth(float health, RenderScope scope) {
|
|
||||||
|
|
||||||
String text = "";
|
|
||||||
|
|
||||||
switch (decimals.value) {
|
|
||||||
case 0:
|
|
||||||
text = String.format("%.0f ", health);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
text = String.format("%.1f ", health);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
text = String.format("%.2f ", health);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.drawText(
|
|
||||||
text,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
dimensions.font.value,
|
|
||||||
dimensions.fgColor.value);
|
|
||||||
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
dimensions.height = 18 * text.split("\n").length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HealthDisplayFeature renders the player's current health as a HUD
|
||||||
|
* element, with configurable display modes (raw health vs. hearts) and
|
||||||
|
* decimal precision.
|
||||||
|
*/
|
||||||
|
public class HealthDisplayFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Integer> displayMode = Property.select(0, "Health", "Hearts");
|
||||||
|
private static final Property<Integer> decimals = Property.select(0, "0", "1", "2");
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(40, 18);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public HealthDisplayFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Health Display", "health")
|
||||||
|
.description("Displays your current health")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
displayMode.named("Display mode"),
|
||||||
|
decimals.named("Decimals"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
float health = modules.player().getHealth();
|
||||||
|
if (displayMode.value == 1)
|
||||||
|
health /= 2f; // convert to hearts
|
||||||
|
renderHealth(health, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderHealth(10f, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderHealth(float health, RenderScope scope) {
|
||||||
|
String fmt = switch (decimals.value) {
|
||||||
|
case 1 -> "%.1f ";
|
||||||
|
case 2 -> "%.2f ";
|
||||||
|
default -> "%.0f ";
|
||||||
|
};
|
||||||
|
|
||||||
|
String text = String.format(fmt, health);
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
layout.height = 18 * text.split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.KeystrokesInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
|
|
||||||
public class Keystrokes extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static final Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static final Property<Boolean> showMouse = Property.bool(true);
|
|
||||||
private static final Property<Boolean> showSpace = Property.bool(false);
|
|
||||||
|
|
||||||
private static final Property<Integer> clickBg = Property.color(0xFFCCCCCC);
|
|
||||||
private static final Property<Integer> clickFg = Property.color(0xFFFFFFFF);
|
|
||||||
|
|
||||||
private static final FeatureLayout dimensions = new FeatureLayout(78, 54);
|
|
||||||
|
|
||||||
private final KeystrokesInterface minecraft;
|
|
||||||
|
|
||||||
// Key states
|
|
||||||
private boolean w, a, s, d, lmb, rmb, space;
|
|
||||||
|
|
||||||
// Layout constants
|
|
||||||
private static final int KEY_SIZE = 24;
|
|
||||||
private static final int KEY_SPACING = 3;
|
|
||||||
private static final int MOUSE_WIDTH = 38;
|
|
||||||
private static final int MOUSE_HEIGHT = 24;
|
|
||||||
private static final int SPACE_HEIGHT = 19;
|
|
||||||
|
|
||||||
public Keystrokes(KeystrokesInterface minecraft) {
|
|
||||||
super(new FeatureDetails("Keystrokes", "keystrokes")
|
|
||||||
.description("Displays movement keystrokes")
|
|
||||||
.version("v0.2.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
showMouse.named("Show mouse clicks"),
|
|
||||||
showSpace.named("Show space"),
|
|
||||||
dimensions.prop(),
|
|
||||||
clickFg.named("Clicked fg"),
|
|
||||||
clickBg.named("Clicked bg"));
|
|
||||||
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
|
|
||||||
dimensions.renderBackground = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
w = minecraft.isForwardPressed();
|
|
||||||
a = minecraft.isLeftPressed();
|
|
||||||
s = minecraft.isBackPressed();
|
|
||||||
d = minecraft.isRightPressed();
|
|
||||||
lmb = minecraft.isAttackPressed();
|
|
||||||
rmb = minecraft.isUsePressed();
|
|
||||||
space = minecraft.isJumpPressed();
|
|
||||||
|
|
||||||
updateHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateHeight() {
|
|
||||||
int height = KEY_SIZE * 2 + KEY_SPACING;
|
|
||||||
|
|
||||||
if (showSpace.value)
|
|
||||||
height += SPACE_HEIGHT + KEY_SPACING;
|
|
||||||
|
|
||||||
if (showMouse.value)
|
|
||||||
height += MOUSE_HEIGHT + KEY_SPACING;
|
|
||||||
|
|
||||||
dimensions.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
render(scope, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
render(scope, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void render(RenderScope scope, boolean live) {
|
|
||||||
|
|
||||||
int yOffset = 0;
|
|
||||||
|
|
||||||
// --- W ---
|
|
||||||
renderKey(scope, live && w, 'W', KEY_SIZE + KEY_SPACING, yOffset);
|
|
||||||
|
|
||||||
// --- A S D ---
|
|
||||||
yOffset += KEY_SIZE + KEY_SPACING;
|
|
||||||
|
|
||||||
renderKey(scope, live && a, 'A', 0, yOffset);
|
|
||||||
renderKey(scope, live && s, 'S', KEY_SIZE + KEY_SPACING, yOffset);
|
|
||||||
renderKey(scope, live && d, 'D', (KEY_SIZE + KEY_SPACING) * 2, yOffset);
|
|
||||||
|
|
||||||
yOffset += KEY_SIZE + KEY_SPACING;
|
|
||||||
|
|
||||||
// --- SPACE ---
|
|
||||||
if (showSpace.value) {
|
|
||||||
renderKeySpace(scope, live && space, 0, yOffset);
|
|
||||||
yOffset += SPACE_HEIGHT + KEY_SPACING;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- MOUSE ---
|
|
||||||
if (showMouse.value) {
|
|
||||||
renderKeyMouse(scope, live && lmb, "LMB", 0, yOffset);
|
|
||||||
renderKeyMouse(scope, live && rmb, "RMB", MOUSE_WIDTH + KEY_SPACING, yOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderKey(RenderScope scope, boolean pressed, char c, int x, int y) {
|
|
||||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
|
||||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
|
||||||
|
|
||||||
scope.drawRoundedRectangle(x, y, KEY_SIZE, KEY_SIZE, dimensions.radius.value, bg);
|
|
||||||
scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, dimensions.font.value, fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderKeyMouse(RenderScope scope, boolean pressed, String text, int x, int y) {
|
|
||||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
|
||||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
|
||||||
|
|
||||||
scope.drawRoundedRectangle(x, y, MOUSE_WIDTH, MOUSE_HEIGHT, dimensions.radius.value, bg);
|
|
||||||
scope.drawText(0.6f, text, x + 9, y + 7, dimensions.font.value, fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderKeySpace(RenderScope scope, boolean pressed, int x, int y) {
|
|
||||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
|
||||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
|
||||||
|
|
||||||
scope.drawRoundedRectangle(x, y, dimensions.width, SPACE_HEIGHT, dimensions.radius.value, bg);
|
|
||||||
|
|
||||||
int lineY = y + SPACE_HEIGHT / 2;
|
|
||||||
scope.drawRect(x + (dimensions.width - 30) / 2, lineY, 30, 1, fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.PlayerModule;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KeystrokesFeature renders the WASD / mouse / space key states as an
|
||||||
|
* on-screen overlay.
|
||||||
|
*/
|
||||||
|
public class KeystrokesFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Configuration
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> showMouse = Property.bool(true);
|
||||||
|
private static final Property<Boolean> showSpace = Property.bool(false);
|
||||||
|
|
||||||
|
private static final Property<Integer> clickBg = Property.color(0xFFCCCCCC);
|
||||||
|
private static final Property<Integer> clickFg = Property.color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(78, 54);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Layout constants
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final int KEY_SIZE = 24;
|
||||||
|
private static final int KEY_SPACING = 3;
|
||||||
|
private static final int MOUSE_WIDTH = 38;
|
||||||
|
private static final int MOUSE_H = 24;
|
||||||
|
private static final int SPACE_H = 19;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Cached key states (updated in tick)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private boolean w, a, s, d, lmb, rmb, space;
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public KeystrokesFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Keystrokes", "keystrokes")
|
||||||
|
.description("Displays movement keystrokes")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
showMouse.named("Show mouse clicks"),
|
||||||
|
showSpace.named("Show space"),
|
||||||
|
layout.prop(),
|
||||||
|
clickFg.named("Clicked fg"),
|
||||||
|
clickBg.named("Clicked bg"));
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
layout.renderBackground = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
PlayerModule player = modules.player();
|
||||||
|
w = player.isForwardPressed();
|
||||||
|
a = player.isLeftPressed();
|
||||||
|
s = player.isBackPressed();
|
||||||
|
d = player.isRightPressed();
|
||||||
|
lmb = player.isAttackPressed();
|
||||||
|
rmb = player.isUsePressed();
|
||||||
|
space = player.isJumpPressed();
|
||||||
|
refreshHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
render(scope, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
render(scope, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void render(RenderScope scope, boolean live) {
|
||||||
|
int yOffset = 0;
|
||||||
|
|
||||||
|
// W (top centre)
|
||||||
|
renderKey(scope, live && w, 'W', KEY_SIZE + KEY_SPACING, yOffset);
|
||||||
|
yOffset += KEY_SIZE + KEY_SPACING;
|
||||||
|
|
||||||
|
// A S D
|
||||||
|
renderKey(scope, live && a, 'A', 0, yOffset);
|
||||||
|
renderKey(scope, live && s, 'S', KEY_SIZE + KEY_SPACING, yOffset);
|
||||||
|
renderKey(scope, live && d, 'D', (KEY_SIZE + KEY_SPACING) * 2, yOffset);
|
||||||
|
yOffset += KEY_SIZE + KEY_SPACING;
|
||||||
|
|
||||||
|
// Spacebar (optional)
|
||||||
|
if (showSpace.value) {
|
||||||
|
renderSpaceKey(scope, live && space, 0, yOffset);
|
||||||
|
yOffset += SPACE_H + KEY_SPACING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse buttons (optional)
|
||||||
|
if (showMouse.value) {
|
||||||
|
renderMouseKey(scope, live && lmb, "LMB", 0, yOffset);
|
||||||
|
renderMouseKey(scope, live && rmb, "RMB", MOUSE_WIDTH + KEY_SPACING, yOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderKey(RenderScope scope, boolean pressed, char c, int x, int y) {
|
||||||
|
int bg = pressed ? clickBg.value : layout.bgColor.value;
|
||||||
|
int fg = pressed ? clickFg.value : layout.fgColor.value;
|
||||||
|
scope.drawRoundedRectangle(x, y, KEY_SIZE, KEY_SIZE, layout.radius.value, bg);
|
||||||
|
scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, layout.font.value, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderMouseKey(RenderScope scope, boolean pressed, String label, int x, int y) {
|
||||||
|
int bg = pressed ? clickBg.value : layout.bgColor.value;
|
||||||
|
int fg = pressed ? clickFg.value : layout.fgColor.value;
|
||||||
|
scope.drawRoundedRectangle(x, y, MOUSE_WIDTH, MOUSE_H, layout.radius.value, bg);
|
||||||
|
scope.drawText(0.6f, label, x + 9, y + 7, layout.font.value, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderSpaceKey(RenderScope scope, boolean pressed, int x, int y) {
|
||||||
|
int bg = pressed ? clickBg.value : layout.bgColor.value;
|
||||||
|
int fg = pressed ? clickFg.value : layout.fgColor.value;
|
||||||
|
scope.drawRoundedRectangle(x, y, layout.width, SPACE_H, layout.radius.value, bg);
|
||||||
|
int lineY = y + SPACE_H / 2;
|
||||||
|
scope.drawRect(x + (layout.width - 30) / 2, lineY, 30, 1, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshHeight() {
|
||||||
|
int h = KEY_SIZE * 2 + KEY_SPACING;
|
||||||
|
if (showSpace.value)
|
||||||
|
h += SPACE_H + KEY_SPACING;
|
||||||
|
if (showMouse.value)
|
||||||
|
h += MOUSE_H + KEY_SPACING;
|
||||||
|
layout.height = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.NametagsInterface;
|
|
||||||
|
|
||||||
public class Nametags extends Feature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Boolean> healthDisplay = Property.bool(false);
|
|
||||||
private static Property<Boolean> players = Property.bool(true);
|
|
||||||
private static Property<Boolean> hostile = Property.bool(false);
|
|
||||||
private static Property<Boolean> passive = Property.bool(false);
|
|
||||||
private static Property<Boolean> heartEmoji = Property.bool(true);
|
|
||||||
private static Property<Boolean> obfuscate = Property.bool(false);
|
|
||||||
private static Property<Integer> unit = Property.select(0, "Health", "Hearts");
|
|
||||||
private static Property<Integer> format = Property.select(0, "Value", "Value / Total", "%");
|
|
||||||
private static Property<Integer> nameColor = Property.select(15,
|
|
||||||
"Black", "Dark Blue", "Dark Green", "Dark Aqua", "Dark Red", "Dark Purple", "Gold", "Gray",
|
|
||||||
"Dark Gray", "Blue", "Green", "Aqua", "Red", "Light Purple", "Yellow", "White");
|
|
||||||
private static Property<Integer> healthColor = Property.select(12,
|
|
||||||
"Black", "Dark Blue", "Dark Green", "Dark Aqua", "Dark Red", "Dark Purple", "Gold", "Gray",
|
|
||||||
"Dark Gray", "Blue", "Green", "Aqua", "Red", "Light Purple", "Yellow", "White");
|
|
||||||
|
|
||||||
private static final String[] COLOR_CODES = {
|
|
||||||
"§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7",
|
|
||||||
"§8", "§9", "§a", "§b", "§c", "§d", "§e", "§f"
|
|
||||||
};
|
|
||||||
|
|
||||||
public Nametags(NametagsInterface minecraft) {
|
|
||||||
super(new FeatureDetails("Nametags", "nametags")
|
|
||||||
.description("Allows you to modify entity nametags")
|
|
||||||
.tags("Camera", "Utility")
|
|
||||||
.version("0.1.0"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
healthDisplay.named("Display health"),
|
|
||||||
players.named("Players"),
|
|
||||||
hostile.named("Hostile"),
|
|
||||||
passive.named("Passive"),
|
|
||||||
heartEmoji.named("Heart emoji"),
|
|
||||||
obfuscate.named("Obfuscate enemy names"),
|
|
||||||
unit.named("Unit"),
|
|
||||||
format.named("Format"),
|
|
||||||
nameColor.named("Name color"),
|
|
||||||
healthColor.named("Health color"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
// Currently unused
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getHealthString(NametagsInterface.EntityState state, float health, float maxHealth) {
|
|
||||||
String ret = "";
|
|
||||||
|
|
||||||
switch (format.value) {
|
|
||||||
case 0: // value
|
|
||||||
ret = String.format("%.1f", health / (unit.value + 1));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // value / total
|
|
||||||
ret = String.format("%.1f", health / (unit.value + 1)) + " / "
|
|
||||||
+ String.format("%.1f", maxHealth / (unit.value + 1));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // %
|
|
||||||
ret = String.format("%.0f%%", (health / maxHealth) * 100);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getNametagString(NametagsInterface.EntityState state) {
|
|
||||||
if (state.getCustomName() == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!healthDisplay.value)
|
|
||||||
return state.getCustomName(); // name only
|
|
||||||
|
|
||||||
float health = state.getHealth();
|
|
||||||
float maxHealth = state.getMaxHealth();
|
|
||||||
NametagsInterface.EntityType type = state.getEntityType();
|
|
||||||
|
|
||||||
if (type == NametagsInterface.EntityType.PASSIVE && !passive.value)
|
|
||||||
return null;
|
|
||||||
if (type == NametagsInterface.EntityType.HOSTILE && !hostile.value)
|
|
||||||
return null;
|
|
||||||
if (type == NametagsInterface.EntityType.PLAYER && !players.value)
|
|
||||||
return null;
|
|
||||||
if (type == NametagsInterface.EntityType.OTHER)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (health < 0f || maxHealth < 0f || health > maxHealth)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
String hpColor = COLOR_CODES[healthColor.value];
|
|
||||||
String color = COLOR_CODES[nameColor.value];
|
|
||||||
String heart = heartEmoji.value ? " ❤" : "";
|
|
||||||
String obf = obfuscate.value ? "§k" : "";
|
|
||||||
String reset = "§r";
|
|
||||||
|
|
||||||
return color + obf + state.getCustomName() + reset + " " + hpColor
|
|
||||||
+ getHealthString(state, health, maxHealth) + heart;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean shouldReplaceName() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.module.EntityModule;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NametagsFeature modifies the nametags displayed above entities.
|
||||||
|
*
|
||||||
|
* Actual rendering is injected via a mixin; this class owns the
|
||||||
|
* configuration and exposes two static query methods the mixin uses:
|
||||||
|
* {@link #shouldReplaceName()} and
|
||||||
|
* {@link #getNametagString(EntityModule.EntityState)}.
|
||||||
|
*
|
||||||
|
* No ModuleProvider is required here because the mixin passes the
|
||||||
|
* already-resolved {@link EntityModule.EntityState} directly.
|
||||||
|
*/
|
||||||
|
public class NametagsFeature extends Feature {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Configuration
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> healthDisplay = Property.bool(false);
|
||||||
|
private static final Property<Boolean> players = Property.bool(true);
|
||||||
|
private static final Property<Boolean> hostile = Property.bool(false);
|
||||||
|
private static final Property<Boolean> passive = Property.bool(false);
|
||||||
|
private static final Property<Boolean> heartEmoji = Property.bool(true);
|
||||||
|
private static final Property<Boolean> obfuscate = Property.bool(false);
|
||||||
|
|
||||||
|
private static final Property<Integer> unit = Property.select(0, "Health", "Hearts");
|
||||||
|
private static final Property<Integer> format = Property.select(0, "Value", "Value / Total", "%");
|
||||||
|
|
||||||
|
private static final Property<Integer> nameColor = Property.select(15,
|
||||||
|
"Black", "Dark Blue", "Dark Green", "Dark Aqua", "Dark Red", "Dark Purple",
|
||||||
|
"Gold", "Gray", "Dark Gray", "Blue", "Green", "Aqua", "Red", "Light Purple", "Yellow", "White");
|
||||||
|
private static final Property<Integer> healthColor = Property.select(12,
|
||||||
|
"Black", "Dark Blue", "Dark Green", "Dark Aqua", "Dark Red", "Dark Purple",
|
||||||
|
"Gold", "Gray", "Dark Gray", "Blue", "Green", "Aqua", "Red", "Light Purple", "Yellow", "White");
|
||||||
|
|
||||||
|
private static final String[] COLOR_CODES = {
|
||||||
|
"§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7",
|
||||||
|
"§8", "§9", "§a", "§b", "§c", "§d", "§e", "§f"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
public NametagsFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Nametags", "nametags")
|
||||||
|
.description("Allows you to modify entity nametags")
|
||||||
|
.tags("Camera", "Utility")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
healthDisplay.named("Display health"),
|
||||||
|
players.named("Players"),
|
||||||
|
hostile.named("Hostile"),
|
||||||
|
passive.named("Passive"),
|
||||||
|
heartEmoji.named("Heart emoji"),
|
||||||
|
obfuscate.named("Obfuscate enemy names"),
|
||||||
|
unit.named("Unit"),
|
||||||
|
format.named("Format"),
|
||||||
|
nameColor.named("Name color"),
|
||||||
|
healthColor.named("Health color"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Mixin query API
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns {@code true} when nametag replacement is active. */
|
||||||
|
public static boolean shouldReplaceName() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the replacement nametag string for the given entity state,
|
||||||
|
* or {@code null} if this entity should not have its tag replaced.
|
||||||
|
*/
|
||||||
|
public static String getNametagString(EntityModule.EntityState state) {
|
||||||
|
if (state.getCustomName() == null)
|
||||||
|
return null;
|
||||||
|
if (!healthDisplay.value)
|
||||||
|
return state.getCustomName();
|
||||||
|
|
||||||
|
float health = state.getHealth();
|
||||||
|
float maxHealth = state.getMaxHealth();
|
||||||
|
|
||||||
|
EntityModule.EntityType type = state.getEntityType();
|
||||||
|
if (type == EntityModule.EntityType.PASSIVE && !passive.value)
|
||||||
|
return null;
|
||||||
|
if (type == EntityModule.EntityType.HOSTILE && !hostile.value)
|
||||||
|
return null;
|
||||||
|
if (type == EntityModule.EntityType.PLAYER && !players.value)
|
||||||
|
return null;
|
||||||
|
if (type == EntityModule.EntityType.OTHER)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (health < 0f || maxHealth < 0f || health > maxHealth)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String hpText = buildHealthString(health, maxHealth);
|
||||||
|
String hpClr = COLOR_CODES[healthColor.value];
|
||||||
|
String nameClr = COLOR_CODES[nameColor.value];
|
||||||
|
String obfCode = obfuscate.value ? "§k" : "";
|
||||||
|
String heart = heartEmoji.value ? " ❤" : "";
|
||||||
|
|
||||||
|
return nameClr + obfCode + state.getCustomName() + "§r "
|
||||||
|
+ hpClr + hpText + heart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private static String buildHealthString(float health, float maxHealth) {
|
||||||
|
float divisor = unit.value + 1;
|
||||||
|
return switch (format.value) {
|
||||||
|
case 1 -> String.format("%.1f", health / divisor)
|
||||||
|
+ " / " + String.format("%.1f", maxHealth / divisor);
|
||||||
|
case 2 -> String.format("%.0f%%", (health / maxHealth) * 100f);
|
||||||
|
default -> String.format("%.1f", health / divisor);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
|
|
||||||
public class NoFog extends Feature {
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Boolean> liquids = Property.bool(false);
|
|
||||||
|
|
||||||
public NoFog() {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("No Fog", "nofog")
|
|
||||||
.description("Stops fog from rendering")
|
|
||||||
.tags("Camera")
|
|
||||||
.version("v0.1.0"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
liquids.named("See through liquids"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static for the mixin
|
|
||||||
public static boolean isActive() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean liquids() {
|
|
||||||
return liquids.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NoFogFeature suppresses distance fog and (optionally) liquid fog.
|
||||||
|
*
|
||||||
|
* Like {@link FullbrightFeature}, the actual suppression is applied
|
||||||
|
* inside a mixin. This class owns the configuration and provides
|
||||||
|
* static query methods the mixin calls.
|
||||||
|
*/
|
||||||
|
public class NoFogFeature extends Feature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> liquids = Property.bool(false);
|
||||||
|
|
||||||
|
public NoFogFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("No Fog", "nofog")
|
||||||
|
.description("Stops fog from rendering")
|
||||||
|
.tags("Camera")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
liquids.named("See through liquids"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Mixin query API
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns {@code true} when fog should be suppressed. */
|
||||||
|
public static boolean isActive() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns {@code true} when liquid-fog suppression is also active. */
|
||||||
|
public static boolean liquids() {
|
||||||
|
return liquids.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.PingInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Ping extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(60, Fonts.getHeight());
|
|
||||||
|
|
||||||
private final PingInterface pingProvider;
|
|
||||||
|
|
||||||
public Ping(PingInterface pingProvider) {
|
|
||||||
super(new FeatureDetails("Ping Display", "ping")
|
|
||||||
.description("Displays ping")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.pingProvider = pingProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
int ping = pingProvider.getPing();
|
|
||||||
String text = ping + " ms";
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
String text = "10 ms";
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PingFeature displays the current server round-trip time in
|
||||||
|
* milliseconds as a HUD element.
|
||||||
|
*/
|
||||||
|
public class PingFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(60, Fonts.getHeight());
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public PingFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Ping Display", "ping")
|
||||||
|
.description("Displays ping to the server")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderPing(modules.network().getPing(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderPing(10, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderPing(int ping, RenderScope scope) {
|
||||||
|
String text = ping + " ms";
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.SpeedometerInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Speedometer extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Integer> axis = Property.select(1, "Absolute", "X axis", "Y axis");
|
|
||||||
private static Property<Integer> unitText = Property.select(0, "None", "Blocks/s", "blocks/s", "b/s");
|
|
||||||
private static Property<Boolean> speedText = Property.bool(true);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(40, 18);
|
|
||||||
|
|
||||||
private final SpeedometerInterface provider;
|
|
||||||
private double speed = 0.;
|
|
||||||
|
|
||||||
public Speedometer(SpeedometerInterface provider) {
|
|
||||||
super(new FeatureDetails("Speedometer", "speed")
|
|
||||||
.description("Displays your current speed")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
axis.named("Speed type"),
|
|
||||||
unitText.named("Unit text"),
|
|
||||||
speedText.named("Show speed text"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderSpeed(2.71, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
SpeedometerInterface.Velocity velocity = provider.getVelocity();
|
|
||||||
boolean onGround = provider.isOnGround();
|
|
||||||
|
|
||||||
double y = onGround ? 0.0 : velocity.y;
|
|
||||||
|
|
||||||
switch (axis.value) {
|
|
||||||
case 0: // Absolute
|
|
||||||
speed = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(y, 2) + Math.pow(velocity.z, 2));
|
|
||||||
break;
|
|
||||||
case 1: // X axis
|
|
||||||
speed = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(velocity.z, 2));
|
|
||||||
break;
|
|
||||||
case 2: // Y axis
|
|
||||||
speed = Math.abs(y);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSpeed(speed * 20, scope); // blocks/tick to blocks/s
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renderSpeed(double speed, RenderScope scope) {
|
|
||||||
String text = String.format("%.2f ", speed);
|
|
||||||
|
|
||||||
switch (unitText.value) {
|
|
||||||
case 1 -> text += "Blocks/s";
|
|
||||||
case 2 -> text += "blocks/s";
|
|
||||||
case 3 -> text += "b/s";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (speedText.value) {
|
|
||||||
text = "Speed: " + text;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
dimensions.height = 18 * text.split("\n").length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.module.PlayerModule;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpeedometerFeature displays the player's current movement speed as
|
||||||
|
* a HUD element. Velocity data comes from {@link PlayerModule}.
|
||||||
|
*/
|
||||||
|
public class SpeedometerFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Integer> axis = Property.select(1, "Absolute", "Horizontal", "Vertical");
|
||||||
|
private static final Property<Integer> unitText = Property.select(0, "None", "Blocks/s", "blocks/s", "b/s");
|
||||||
|
private static final Property<Boolean> showLabel = Property.bool(true);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(40, 18);
|
||||||
|
|
||||||
|
private double cachedSpeed = 0.0;
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public SpeedometerFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Speedometer", "speed")
|
||||||
|
.description("Displays your current movement speed")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
axis.named("Speed type"),
|
||||||
|
unitText.named("Unit text"),
|
||||||
|
showLabel.named("Show speed label"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
PlayerModule player = modules.player();
|
||||||
|
PlayerModule.Velocity v = player.getVelocity();
|
||||||
|
boolean onGround = player.isOnGround();
|
||||||
|
|
||||||
|
double effectiveY = onGround ? 0.0 : v.y;
|
||||||
|
|
||||||
|
cachedSpeed = switch (axis.value) {
|
||||||
|
case 0 -> Math.sqrt(v.x * v.x + effectiveY * effectiveY + v.z * v.z); // absolute 3-D
|
||||||
|
case 2 -> Math.abs(effectiveY); // vertical only
|
||||||
|
default -> Math.sqrt(v.x * v.x + v.z * v.z); // horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSpeed(cachedSpeed * 20.0, scope); // blocks/tick → blocks/s
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderSpeed(2.71, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderSpeed(double bps, RenderScope scope) {
|
||||||
|
String text = String.format("%.2f ", bps);
|
||||||
|
|
||||||
|
text += switch (unitText.value) {
|
||||||
|
case 1 -> "Blocks/s";
|
||||||
|
case 2 -> "blocks/s";
|
||||||
|
case 3 -> "b/s";
|
||||||
|
default -> "";
|
||||||
|
};
|
||||||
|
|
||||||
|
if (showLabel.value) {
|
||||||
|
text = "Speed: " + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
layout.height = 18 * text.split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.feature.interfaces.StatusEffectsInterface;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
|
|
||||||
public class StatusEffects extends Feature implements HudFeature {
|
|
||||||
|
|
||||||
public static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(60, 0);
|
|
||||||
|
|
||||||
private final StatusEffectsInterface provider;
|
|
||||||
|
|
||||||
public StatusEffects(StatusEffectsInterface provider) {
|
|
||||||
super(new FeatureDetails("Status effects", "effect")
|
|
||||||
.description("Displays status effects")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
dimensions.prop());
|
|
||||||
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderEffects(RenderScope scope, List<StatusEffectsInterface.Effect> effects) {
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
for (StatusEffectsInterface.Effect effect : effects) {
|
|
||||||
if (!effect.shouldShowIcon())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
scope.drawSpriteStretched(effect.getIcon(), 0, 18 * row, 16, 16);
|
|
||||||
scope.drawText(0.5f, getDurationAsString(effect), 18, 18 * row + 3, dimensions.font.value, -1);
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
dimensions.height = 18 * row;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
renderEffects(scope, provider.getActiveEffects());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
renderEffects(scope, provider.getDummyEffects());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDurationAsString(StatusEffectsInterface.Effect effect) {
|
|
||||||
if (effect.isInfinite()) {
|
|
||||||
return effect.getInfiniteText();
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalSeconds = effect.getDurationSeconds();
|
|
||||||
if (totalSeconds >= 3600) {
|
|
||||||
return totalSeconds / 3600 + "h";
|
|
||||||
} else if (totalSeconds >= 60) {
|
|
||||||
int minutes = totalSeconds / 60;
|
|
||||||
int seconds = totalSeconds % 60;
|
|
||||||
return String.format("%d:%02d", minutes, seconds);
|
|
||||||
} else {
|
|
||||||
return totalSeconds + "s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.ModuleProvider;
|
||||||
|
import org.saturnclient.common.ref.asset.SpriteRef;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusEffectsFeature displays the player's active potion effects as
|
||||||
|
* a HUD overlay.
|
||||||
|
*
|
||||||
|
* The effect list is supplied by the platform through an interface
|
||||||
|
* returned by a future {@code PlayerModule#getActiveEffects()} call.
|
||||||
|
* Until that is wired up, the existing {@link StatusEffectsInterface}
|
||||||
|
* bridge is used, accessed via {@link ModuleProvider}.
|
||||||
|
*/
|
||||||
|
public class StatusEffectsFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
public static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(60, 0);
|
||||||
|
|
||||||
|
private final ModuleProvider modules;
|
||||||
|
|
||||||
|
public StatusEffectsFeature(ModuleProvider modules) {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Status Effects", "effect")
|
||||||
|
.description("Displays active status effects")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
layout.prop());
|
||||||
|
|
||||||
|
this.modules = modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Effect data contract (inner interface keeps things self-contained)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal view of a single active status effect.
|
||||||
|
* Implementations are provided by the platform bridge.
|
||||||
|
*/
|
||||||
|
public interface EffectView {
|
||||||
|
boolean shouldShowIcon();
|
||||||
|
|
||||||
|
SpriteRef getIcon();
|
||||||
|
|
||||||
|
boolean isInfinite();
|
||||||
|
|
||||||
|
String getInfiniteText();
|
||||||
|
|
||||||
|
int getDurationSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderEffects(scope, modules.player().getActiveEffects());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderEffects(scope, modules.player().getDummyEffects());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderEffects(RenderScope scope, List<? extends EffectView> effects) {
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
for (EffectView effect : effects) {
|
||||||
|
if (!effect.shouldShowIcon())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
scope.drawSpriteStretched(effect.getIcon(), 0, 18 * row, 16, 16);
|
||||||
|
scope.drawText(0.5f, durationString(effect),
|
||||||
|
18, 18 * row + 3,
|
||||||
|
layout.font.value, -1);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.height = 18 * row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String durationString(EffectView effect) {
|
||||||
|
if (effect.isInfinite())
|
||||||
|
return effect.getInfiniteText();
|
||||||
|
|
||||||
|
int totalSeconds = effect.getDurationSeconds();
|
||||||
|
if (totalSeconds >= 3600) {
|
||||||
|
return (totalSeconds / 3600) + "h";
|
||||||
|
} else if (totalSeconds >= 60) {
|
||||||
|
return String.format("%d:%02d", totalSeconds / 60, totalSeconds % 60);
|
||||||
|
} else {
|
||||||
|
return totalSeconds + "s";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.HudFeature;
|
|
||||||
import org.saturnclient.feature.FeatureLayout;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
|
||||||
import org.saturnclient.ui.resources.Fonts;
|
|
||||||
|
|
||||||
public class Tps extends Feature implements HudFeature {
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static FeatureLayout dimensions = new FeatureLayout(60, Fonts.getHeight());
|
|
||||||
|
|
||||||
private static long lastWorldAge = -1;
|
|
||||||
private static long lastRealTime = -1;
|
|
||||||
private static double currentTps = 20.0;
|
|
||||||
|
|
||||||
private static final int SAMPLE_COUNT = 2;
|
|
||||||
private static final double[] samples = new double[SAMPLE_COUNT];
|
|
||||||
static {
|
|
||||||
java.util.Arrays.fill(samples, 20.0);
|
|
||||||
}
|
|
||||||
private static int sampleIndex = 0;
|
|
||||||
private static int samplesFilled = 0;
|
|
||||||
|
|
||||||
public Tps() {
|
|
||||||
super(new FeatureDetails("TPS Display", "tps")
|
|
||||||
.description("Displays the TPS")
|
|
||||||
.version("v0.1.0")
|
|
||||||
.tags("Utility"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
dimensions.prop());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onTimePacket(long worldAge) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if (lastWorldAge != -1 && lastRealTime != -1) {
|
|
||||||
long tickDelta = worldAge - lastWorldAge;
|
|
||||||
long timeDelta = now - lastRealTime;
|
|
||||||
|
|
||||||
if (timeDelta > 0 && tickDelta > 0) {
|
|
||||||
double sample = (tickDelta / (double) timeDelta) * 1000.0;
|
|
||||||
sample = Math.min(sample, 20.0);
|
|
||||||
|
|
||||||
samples[sampleIndex % SAMPLE_COUNT] = sample;
|
|
||||||
sampleIndex++;
|
|
||||||
samplesFilled = Math.min(samplesFilled + 1, SAMPLE_COUNT);
|
|
||||||
|
|
||||||
double sum = 0;
|
|
||||||
for (int i = 0; i < samplesFilled; i++)
|
|
||||||
sum += samples[i];
|
|
||||||
currentTps = sum / samplesFilled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastWorldAge = worldAge;
|
|
||||||
lastRealTime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reset() {
|
|
||||||
lastWorldAge = -1;
|
|
||||||
lastRealTime = -1;
|
|
||||||
currentTps = 20.0;
|
|
||||||
sampleIndex = 0;
|
|
||||||
samplesFilled = SAMPLE_COUNT;
|
|
||||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
|
||||||
samples[i] = 20.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderHud(RenderScope scope) {
|
|
||||||
String text = String.format("%.1f TPS", currentTps);
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderDummy(RenderScope scope) {
|
|
||||||
String text = "20.0 TPS";
|
|
||||||
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
|
|
||||||
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureLayout getDimensions() {
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.ui.RenderScope;
|
||||||
|
import org.saturnclient.ui.resources.Fonts;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
import org.saturnclient.feature.FeatureLayout;
|
||||||
|
import org.saturnclient.feature.HudFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TpsFeature estimates the server's ticks-per-second by measuring the
|
||||||
|
* elapsed real time between consecutive world-time packets.
|
||||||
|
*
|
||||||
|
* TPS calculation is event-driven: the networking layer (or mixin)
|
||||||
|
* calls {@link #onTimePacket(long)} whenever a time-update packet
|
||||||
|
* arrives. No ModuleProvider is required because there is no
|
||||||
|
* per-frame polling of the engine.
|
||||||
|
*/
|
||||||
|
public class TpsFeature extends Feature implements HudFeature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final FeatureLayout layout = new FeatureLayout(60, Fonts.getHeight());
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// TPS sampling state (static — shared across all instances)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final int SAMPLE_COUNT = 2;
|
||||||
|
private static final double[] samples = new double[SAMPLE_COUNT];
|
||||||
|
|
||||||
|
static {
|
||||||
|
java.util.Arrays.fill(samples, 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long lastWorldAge = -1L;
|
||||||
|
private static long lastRealTime = -1L;
|
||||||
|
private static double currentTps = 20.0;
|
||||||
|
private static int sampleIndex = 0;
|
||||||
|
private static int samplesFilled = SAMPLE_COUNT;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
public TpsFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("TPS Display", "tps")
|
||||||
|
.description("Displays the server's ticks per second")
|
||||||
|
.version("v0.2.0")
|
||||||
|
.tags("Utility"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
layout.prop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Packet hook (called by the networking mixin)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called every time the client receives a world-time packet
|
||||||
|
* carrying the updated {@code worldAge} value.
|
||||||
|
*/
|
||||||
|
public static void onTimePacket(long worldAge) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (lastWorldAge != -1L && lastRealTime != -1L) {
|
||||||
|
long tickDelta = worldAge - lastWorldAge;
|
||||||
|
long timeDelta = now - lastRealTime;
|
||||||
|
|
||||||
|
if (timeDelta > 0 && tickDelta > 0) {
|
||||||
|
double sample = Math.min((tickDelta / (double) timeDelta) * 1000.0, 20.0);
|
||||||
|
|
||||||
|
samples[sampleIndex % SAMPLE_COUNT] = sample;
|
||||||
|
sampleIndex++;
|
||||||
|
samplesFilled = Math.min(samplesFilled + 1, SAMPLE_COUNT);
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for (int i = 0; i < samplesFilled; i++)
|
||||||
|
sum += samples[i];
|
||||||
|
currentTps = sum / samplesFilled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWorldAge = worldAge;
|
||||||
|
lastRealTime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resets TPS history (e.g. on server disconnect). */
|
||||||
|
public static void reset() {
|
||||||
|
lastWorldAge = -1L;
|
||||||
|
lastRealTime = -1L;
|
||||||
|
currentTps = 20.0;
|
||||||
|
sampleIndex = 0;
|
||||||
|
samplesFilled = SAMPLE_COUNT;
|
||||||
|
java.util.Arrays.fill(samples, 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// HudFeature
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderHud(RenderScope scope) {
|
||||||
|
renderTps(currentTps, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderDummy(RenderScope scope) {
|
||||||
|
renderTps(20.0, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Rendering
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderTps(double tps, RenderScope scope) {
|
||||||
|
String text = String.format("%.1f TPS", tps);
|
||||||
|
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
|
||||||
|
layout.width = Fonts.getWidth(text, layout.font.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureLayout getDimensions() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package org.saturnclient.feature.features;
|
|
||||||
|
|
||||||
import org.saturnclient.common.provider.GLFWProvider;
|
|
||||||
import org.saturnclient.config.manager.Property;
|
|
||||||
import org.saturnclient.feature.Feature;
|
|
||||||
import org.saturnclient.feature.FeatureDetails;
|
|
||||||
|
|
||||||
public class Zoom extends Feature {
|
|
||||||
private static Property<Boolean> enabled = Property.bool(false);
|
|
||||||
private static Property<Boolean> toggle = Property.bool(false);
|
|
||||||
public static Property<Integer> zoomKey = Property.keybinding(GLFWProvider.GLFW_KEY_C);
|
|
||||||
public static Property<Float> zoomLevel = Property.floatProp(3.0f);
|
|
||||||
|
|
||||||
public static boolean isZooming = false;
|
|
||||||
|
|
||||||
public Zoom() {
|
|
||||||
super(
|
|
||||||
new FeatureDetails("Zoom", "zoom")
|
|
||||||
.description("Allows you to zoom in for a closer view")
|
|
||||||
.tags("Camera", "Utility")
|
|
||||||
.version("0.1.0"),
|
|
||||||
enabled.named("Enabled"),
|
|
||||||
toggle.named("Toggle zoom"),
|
|
||||||
zoomKey.named("Zoom Keybinding"),
|
|
||||||
zoomLevel.named("Zoom Level"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
if (toggle.value) {
|
|
||||||
if (zoomKey.wasKeyPressed()) {
|
|
||||||
isZooming = !isZooming;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
isZooming = zoomKey.isKeyPressed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float getZoomLevel() {
|
|
||||||
return zoomLevel.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean shouldZoom() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnabled(boolean e) {
|
|
||||||
enabled.value = e;
|
|
||||||
if (!e) {
|
|
||||||
isZooming = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package org.saturnclient.feature.features;
|
||||||
|
|
||||||
|
import org.saturnclient.common.provider.GLFWProvider;
|
||||||
|
import org.saturnclient.config.manager.Property;
|
||||||
|
import org.saturnclient.feature.Feature;
|
||||||
|
import org.saturnclient.feature.FeatureDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ZoomFeature multiplies the camera's field-of-view to create an
|
||||||
|
* optical-zoom effect.
|
||||||
|
*
|
||||||
|
* The FOV override is applied inside a mixin that polls
|
||||||
|
* {@link #shouldZoom()} and {@link #getZoomLevel()} each frame.
|
||||||
|
* No ModuleProvider is needed — there is no per-tick engine query.
|
||||||
|
*/
|
||||||
|
public class ZoomFeature extends Feature {
|
||||||
|
|
||||||
|
private static final Property<Boolean> enabled = Property.bool(false);
|
||||||
|
private static final Property<Boolean> toggle = Property.bool(false);
|
||||||
|
public static final Property<Integer> zoomKey = Property.keybinding(GLFWProvider.GLFW_KEY_C);
|
||||||
|
public static final Property<Float> zoomLevel = Property.floatProp(3.0f);
|
||||||
|
|
||||||
|
/** Whether zoom is currently active (read by the FOV mixin). */
|
||||||
|
public static boolean isZooming = false;
|
||||||
|
|
||||||
|
public ZoomFeature() {
|
||||||
|
super(
|
||||||
|
new FeatureDetails("Zoom", "zoom")
|
||||||
|
.description("Zoom in for a closer view")
|
||||||
|
.tags("Camera", "Utility")
|
||||||
|
.version("v0.2.0"),
|
||||||
|
enabled.named("Enabled"),
|
||||||
|
toggle.named("Toggle zoom"),
|
||||||
|
zoomKey.named("Zoom Keybinding"),
|
||||||
|
zoomLevel.named("Zoom Level"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Feature contract
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (toggle.value) {
|
||||||
|
if (zoomKey.wasKeyPressed()) {
|
||||||
|
isZooming = !isZooming;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isZooming = zoomKey.isKeyPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnabled(boolean e) {
|
||||||
|
enabled.value = e;
|
||||||
|
if (!e)
|
||||||
|
isZooming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Mixin query API
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns {@code true} when the FOV override should be applied. */
|
||||||
|
public static boolean shouldZoom() {
|
||||||
|
return enabled.value && isZooming;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the zoom divisor (e.g. 3.0 → one-third of normal FOV). */
|
||||||
|
public static float getZoomLevel() {
|
||||||
|
return zoomLevel.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
import org.saturnclient.common.ref.game.ItemStackRef;
|
|
||||||
|
|
||||||
public interface ArmorDisplayInterface {
|
|
||||||
|
|
||||||
ItemStackRef getMainHand();
|
|
||||||
|
|
||||||
ItemStackRef getHelmet();
|
|
||||||
|
|
||||||
ItemStackRef getChestplate();
|
|
||||||
|
|
||||||
ItemStackRef getLeggings();
|
|
||||||
|
|
||||||
ItemStackRef getBoots();
|
|
||||||
|
|
||||||
ItemStackRef getDummyMainHand();
|
|
||||||
|
|
||||||
ItemStackRef getDummyHelmet();
|
|
||||||
|
|
||||||
ItemStackRef getDummyChestplate();
|
|
||||||
|
|
||||||
ItemStackRef getDummyLeggings();
|
|
||||||
|
|
||||||
ItemStackRef getDummyBoots();
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface AutoSprintInterface {
|
|
||||||
|
|
||||||
boolean hasPlayer();
|
|
||||||
|
|
||||||
boolean hasNetwork();
|
|
||||||
|
|
||||||
boolean isForwardPressed();
|
|
||||||
|
|
||||||
boolean isBackPressed();
|
|
||||||
|
|
||||||
boolean isSneaking();
|
|
||||||
|
|
||||||
boolean hasHorizontalCollision();
|
|
||||||
|
|
||||||
boolean isUsingItem();
|
|
||||||
|
|
||||||
void setSprinting(boolean sprinting);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface CoordinatesInterface {
|
|
||||||
|
|
||||||
int getPlayerX();
|
|
||||||
|
|
||||||
int getPlayerY();
|
|
||||||
|
|
||||||
int getPlayerZ();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface CrosshairInterface {
|
|
||||||
|
|
||||||
boolean isTargetingLivingEntity();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface DayCounterInterface {
|
|
||||||
|
|
||||||
long getDay();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface FpsInterface {
|
|
||||||
|
|
||||||
int getFps();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface FreelookInterface {
|
|
||||||
|
|
||||||
boolean isFirstPerson();
|
|
||||||
|
|
||||||
void setFirstPerson();
|
|
||||||
|
|
||||||
void setThirdPersonBack();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface HealthDisplayInterface {
|
|
||||||
|
|
||||||
float getPlayerHealth();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface KeystrokesInterface {
|
|
||||||
|
|
||||||
boolean isForwardPressed();
|
|
||||||
boolean isBackPressed();
|
|
||||||
boolean isLeftPressed();
|
|
||||||
boolean isRightPressed();
|
|
||||||
boolean isJumpPressed();
|
|
||||||
boolean isAttackPressed();
|
|
||||||
boolean isUsePressed();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface NametagsInterface {
|
|
||||||
|
|
||||||
public static interface EntityState {
|
|
||||||
|
|
||||||
String getCustomName();
|
|
||||||
|
|
||||||
float getHealth();
|
|
||||||
|
|
||||||
float getMaxHealth();
|
|
||||||
|
|
||||||
EntityType getEntityType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static enum EntityType {
|
|
||||||
PLAYER,
|
|
||||||
PASSIVE,
|
|
||||||
HOSTILE,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface PingInterface {
|
|
||||||
|
|
||||||
/** Returns current player ping in milliseconds */
|
|
||||||
int getPing();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
public interface SpeedometerInterface {
|
|
||||||
|
|
||||||
/** Returns the current player or vehicle velocity in blocks per tick */
|
|
||||||
Velocity getVelocity();
|
|
||||||
|
|
||||||
/** Returns true if player or vehicle is on ground */
|
|
||||||
boolean isOnGround();
|
|
||||||
|
|
||||||
class Velocity {
|
|
||||||
public final double x, y, z;
|
|
||||||
|
|
||||||
public Velocity(double x, double y, double z) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package org.saturnclient.feature.interfaces;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.saturnclient.common.ref.asset.SpriteRef;
|
|
||||||
|
|
||||||
public interface StatusEffectsInterface {
|
|
||||||
|
|
||||||
/** Returns a list of currently active effects */
|
|
||||||
List<Effect> getActiveEffects();
|
|
||||||
|
|
||||||
/** Returns a dummy list of effects for dummy rendering */
|
|
||||||
List<Effect> getDummyEffects();
|
|
||||||
|
|
||||||
/** Represents a single effect for rendering purposes */
|
|
||||||
class Effect {
|
|
||||||
private final SpriteRef icon;
|
|
||||||
private final int durationSeconds;
|
|
||||||
private final boolean infinite;
|
|
||||||
private final boolean showIcon;
|
|
||||||
private final String infiniteText;
|
|
||||||
|
|
||||||
public Effect(SpriteRef icon, int durationSeconds, boolean infinite, boolean showIcon, String infiniteText) {
|
|
||||||
this.icon = icon;
|
|
||||||
this.durationSeconds = durationSeconds;
|
|
||||||
this.infinite = infinite;
|
|
||||||
this.showIcon = showIcon;
|
|
||||||
this.infiniteText = infiniteText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpriteRef getIcon() {
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDurationSeconds() {
|
|
||||||
return durationSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInfinite() {
|
|
||||||
return infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldShowIcon() {
|
|
||||||
return showIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInfiniteText() {
|
|
||||||
return infiniteText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user