Update to 1.21.6
This commit is contained in:
@@ -73,6 +73,7 @@ Code inspired by Inferis!
|
|||||||
|
|
||||||
## Client Side
|
## Client Side
|
||||||
- [x] Fixed bug with too high zoom step values
|
- [x] Fixed bug with too high zoom step values
|
||||||
|
- [x] Rewrote config screen using YACL
|
||||||
|
|
||||||
## Server Side
|
## Server Side
|
||||||
- [x] Telekinesis
|
- [x] Telekinesis
|
||||||
@@ -83,7 +84,6 @@ Code inspired by Inferis!
|
|||||||
# To-do
|
# To-do
|
||||||
|
|
||||||
## General
|
## General
|
||||||
- Rework config system
|
|
||||||
- Store server settings in world folder for better singleplayer use
|
- Store server settings in world folder for better singleplayer use
|
||||||
|
|
||||||
## Client side
|
## Client side
|
||||||
|
@@ -15,6 +15,10 @@ repositories {
|
|||||||
name = "Terraformers"
|
name = "Terraformers"
|
||||||
url = "https://maven.terraformersmc.com/"
|
url = "https://maven.terraformersmc.com/"
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name = "Xander Maven"
|
||||||
|
url = "https://maven.isxander.dev/releases"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
@@ -43,6 +47,7 @@ dependencies {
|
|||||||
|
|
||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
|
modImplementation "dev.isxander:yet-another-config-lib:${project.yacl_version}"
|
||||||
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}")
|
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,16 +4,17 @@ org.gradle.parallel=true
|
|||||||
|
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.5
|
minecraft_version=1.21.6
|
||||||
yarn_mappings=1.21.5+build.1
|
yarn_mappings=1.21.6+build.1
|
||||||
loader_version=0.16.10
|
loader_version=0.16.14
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.4.1
|
mod_version=1.4.2
|
||||||
maven_group=wtf.hak.survivalfabric
|
maven_group=wtf.hak.survivalfabric
|
||||||
archives_base_name=survivalfabric
|
archives_base_name=survivalfabric
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
fabric_version=0.119.5+1.21.5
|
fabric_version=0.127.0+1.21.6
|
||||||
|
yacl_version=3.7.1+1.21.6-fabric
|
||||||
|
|
||||||
modmenu_version=14.0.0-rc.2
|
modmenu_version=15.0.0-beta.1
|
@@ -32,9 +32,7 @@ public class Zoom {
|
|||||||
MinecraftClient.getInstance().options.smoothCameraEnabled = true;
|
MinecraftClient.getInstance().options.smoothCameraEnabled = true;
|
||||||
}
|
}
|
||||||
SHOULD_ZOOM = true;
|
SHOULD_ZOOM = true;
|
||||||
client.gameRenderer.setRenderHand(false);
|
|
||||||
} else if (!ZOOM_BIND.isPressed() && SHOULD_ZOOM) {
|
} else if (!ZOOM_BIND.isPressed() && SHOULD_ZOOM) {
|
||||||
client.gameRenderer.setRenderHand(true);
|
|
||||||
SHOULD_ZOOM = false;
|
SHOULD_ZOOM = false;
|
||||||
ZOOM_STEP = 0;
|
ZOOM_STEP = 0;
|
||||||
if(getConfig().smoothCamera) {
|
if(getConfig().smoothCamera) {
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
package wtf.hak.survivalfabric.mixin.client;
|
|
||||||
|
|
||||||
import net.minecraft.block.enums.CameraSubmersionType;
|
|
||||||
import net.minecraft.client.render.BackgroundRenderer;
|
|
||||||
import net.minecraft.client.render.Camera;
|
|
||||||
import net.minecraft.client.render.Fog;
|
|
||||||
import net.minecraft.client.render.FogShape;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import org.joml.Vector4f;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig;
|
|
||||||
|
|
||||||
@Mixin(value = BackgroundRenderer.class, priority = 910)
|
|
||||||
public class BackgroundRendererMixin {
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private static final Fog EMPTY_FOG = new Fog(-8.0f, 1_000_000.0F, FogShape.CYLINDER, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
@Inject(method = "applyFog", at = @At("RETURN"), cancellable = true)
|
|
||||||
private static void applyFog(Camera camera, BackgroundRenderer.FogType fogType, Vector4f color, float viewDistance, boolean thickenFog, float tickProgress, CallbackInfoReturnable<Fog> cir) {
|
|
||||||
CameraSubmersionType submersion = camera.getSubmersionType();
|
|
||||||
boolean renderFog = true;
|
|
||||||
|
|
||||||
switch (submersion) {
|
|
||||||
case NONE -> {
|
|
||||||
World world = camera.getFocusedEntity().getWorld();
|
|
||||||
if ((world.getRegistryKey() == World.OVERWORLD && !getConfig().renderOverworldFog)
|
|
||||||
|| (world.getRegistryKey() == World.NETHER && !getConfig().renderNetherFog)
|
|
||||||
|| (world.getRegistryKey() == World.END && !getConfig().renderEndFog)) {
|
|
||||||
renderFog = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case WATER -> renderFog = getConfig().renderWaterFog;
|
|
||||||
case LAVA -> renderFog = getConfig().renderLavaFog;
|
|
||||||
case POWDER_SNOW -> renderFog = getConfig().renderSnowFog;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderFog) {
|
|
||||||
cir.setReturnValue(EMPTY_FOG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,80 @@
|
|||||||
|
package wtf.hak.survivalfabric.mixin.client;
|
||||||
|
|
||||||
|
|
||||||
|
import net.minecraft.client.render.RenderTickCounter;
|
||||||
|
import net.minecraft.client.render.fog.*;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig;
|
||||||
|
|
||||||
|
public class FogMixins {
|
||||||
|
|
||||||
|
@Mixin(DimensionOrBossFogModifier.class)
|
||||||
|
static class DimensionOrBossFogModifierMixin {
|
||||||
|
@Inject(at = @At("TAIL"), method = "applyStartEndModifier")
|
||||||
|
public void disableDimensionFog(FogData data, Entity cameraEntity, BlockPos cameraPos, ClientWorld world, float viewDistance, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||||
|
boolean shouldDisable = false;
|
||||||
|
|
||||||
|
if (world.getRegistryKey() == World.NETHER && !getConfig().renderNetherFog) {
|
||||||
|
shouldDisable = true;
|
||||||
|
} else if (world.getRegistryKey() == World.END && !getConfig().renderEndFog) {
|
||||||
|
shouldDisable = true;
|
||||||
|
} else if (world.getRegistryKey() == World.OVERWORLD && !getConfig().renderOverworldFog) {
|
||||||
|
shouldDisable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldDisable) {
|
||||||
|
data.environmentalStart = Float.MAX_VALUE;
|
||||||
|
data.environmentalEnd = Float.MAX_VALUE;
|
||||||
|
data.skyEnd = Float.MAX_VALUE;
|
||||||
|
data.cloudEnd = Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mixin(WaterFogModifier.class)
|
||||||
|
static class WaterFogModifierMixin {
|
||||||
|
@Inject(at = @At("TAIL"), method = "applyStartEndModifier")
|
||||||
|
public void disableWaterFog(FogData data, Entity cameraEntity, BlockPos cameraPos, ClientWorld world, float viewDistance, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||||
|
if (!getConfig().renderWaterFog) {
|
||||||
|
data.environmentalStart = Float.MAX_VALUE;
|
||||||
|
data.environmentalEnd = Float.MAX_VALUE;
|
||||||
|
data.skyEnd = Float.MAX_VALUE;
|
||||||
|
data.cloudEnd = Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mixin(LavaFogModifier.class)
|
||||||
|
static class LavaFogModifierMixin {
|
||||||
|
@Inject(at = @At("TAIL"), method = "applyStartEndModifier")
|
||||||
|
public void disableLavaFog(FogData data, Entity cameraEntity, BlockPos cameraPos, ClientWorld world, float viewDistance, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||||
|
if (!getConfig().renderLavaFog) {
|
||||||
|
data.environmentalStart = Float.MAX_VALUE;
|
||||||
|
data.environmentalEnd = Float.MAX_VALUE;
|
||||||
|
data.skyEnd = Float.MAX_VALUE;
|
||||||
|
data.cloudEnd = Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mixin(PowderSnowFogModifier.class)
|
||||||
|
static class PowderSnowFogModifierMixin {
|
||||||
|
@Inject(at = @At("TAIL"), method = "applyStartEndModifier")
|
||||||
|
public void disablePowderSnowFog(FogData data, Entity cameraEntity, BlockPos cameraPos, ClientWorld world, float viewDistance, RenderTickCounter tickCounter, CallbackInfo ci) {
|
||||||
|
if (!getConfig().renderSnowFog) {
|
||||||
|
data.environmentalStart = Float.MAX_VALUE;
|
||||||
|
data.environmentalEnd = Float.MAX_VALUE;
|
||||||
|
data.skyEnd = Float.MAX_VALUE;
|
||||||
|
data.cloudEnd = Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,14 @@
|
|||||||
package wtf.hak.survivalfabric.mixin.client;
|
package wtf.hak.survivalfabric.mixin.client;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElement;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
import net.minecraft.client.render.GameRenderer;
|
import net.minecraft.client.render.GameRenderer;
|
||||||
|
import net.minecraft.client.render.RenderTickCounter;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import wtf.hak.survivalfabric.features.Zoom;
|
import wtf.hak.survivalfabric.features.Zoom;
|
||||||
@@ -18,4 +24,13 @@ public class GameRendererMixin {
|
|||||||
return Zoom.isZooming() ? Math.clamp(Zoom.getZoomFov(), 1, 110) : fov;
|
return Zoom.isZooming() ? Math.clamp(Zoom.getZoomFov(), 1, 110) : fov;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure hand only gets rendered when NOT zooming.
|
||||||
|
*/
|
||||||
|
@WrapMethod(method = "renderHand")
|
||||||
|
public void render(float tickProgress, boolean sleeping, Matrix4f positionMatrix, Operation<Void> original) {
|
||||||
|
if(!Zoom.isZooming())
|
||||||
|
original.call(tickProgress, sleeping, positionMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,599 +0,0 @@
|
|||||||
package wtf.hak.survivalfabric.modmenu;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.DrawContext;
|
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
|
||||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.math.MathHelper;
|
|
||||||
import wtf.hak.survivalfabric.config.client.ClientConfig;
|
|
||||||
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ConfigScreen extends Screen {
|
|
||||||
|
|
||||||
private static final int OPTION_HEIGHT = 25;
|
|
||||||
private static final int SCROLL_BAR_WIDTH = 6;
|
|
||||||
private static final int TOP_PADDING = 40;
|
|
||||||
private static final int BOTTOM_PADDING = 35;
|
|
||||||
private static final int SIDE_PADDING = 20;
|
|
||||||
private final Screen parent;
|
|
||||||
private final List<ConfigOption<?>> options = new ArrayList<>();
|
|
||||||
private TextFieldWidget activeTextField = null;
|
|
||||||
private float scrollPosition = 0.0F;
|
|
||||||
private boolean scrolling = false;
|
|
||||||
private int contentHeight = 0;
|
|
||||||
|
|
||||||
public ConfigScreen(Screen parent) {
|
|
||||||
super(Text.literal("Survival Fabric - Client Config"));
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init() {
|
|
||||||
options.clear();
|
|
||||||
|
|
||||||
int listWidth = this.width - (SIDE_PADDING * 2);
|
|
||||||
int listHeight = this.height - TOP_PADDING - BOTTOM_PADDING;
|
|
||||||
int listLeft = SIDE_PADDING;
|
|
||||||
int listTop = TOP_PADDING;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for (Field field : ClientConfig.class.getFields()) {
|
|
||||||
try {
|
|
||||||
Class<?> type = field.getType();
|
|
||||||
String name = formatFieldName(field.getName());
|
|
||||||
|
|
||||||
ConfigOption<?> option = null;
|
|
||||||
if (type == boolean.class) {
|
|
||||||
boolean value = field.getBoolean(ClientConfigManager.getConfig());
|
|
||||||
option = new BooleanConfigOption(
|
|
||||||
name,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
listLeft,
|
|
||||||
listTop + (index * OPTION_HEIGHT) - (int) scrollPosition,
|
|
||||||
listWidth
|
|
||||||
);
|
|
||||||
} else if (type == int.class) {
|
|
||||||
int value = field.getInt(ClientConfigManager.getConfig());
|
|
||||||
option = new IntegerConfigOption(
|
|
||||||
name,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
listLeft,
|
|
||||||
listTop + (index * OPTION_HEIGHT) - (int) scrollPosition,
|
|
||||||
listWidth
|
|
||||||
);
|
|
||||||
} else if (type == float.class) {
|
|
||||||
float value = field.getFloat(ClientConfigManager.getConfig());
|
|
||||||
option = new FloatConfigOption(
|
|
||||||
name,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
listLeft,
|
|
||||||
listTop + (index * OPTION_HEIGHT) - (int) scrollPosition,
|
|
||||||
listWidth
|
|
||||||
);
|
|
||||||
} else if (type == String.class) {
|
|
||||||
String value = (String) field.get(ClientConfigManager.getConfig());
|
|
||||||
option = new StringConfigOption(
|
|
||||||
name,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
listLeft,
|
|
||||||
listTop + (index * OPTION_HEIGHT) - (int) scrollPosition,
|
|
||||||
listWidth
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option != null) {
|
|
||||||
options.add(option);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ConfigOption<?> option : options) {
|
|
||||||
if (option instanceof NumericConfigOption) {
|
|
||||||
((NumericConfigOption<?>) option).createTextField(this.client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentHeight = options.size() * OPTION_HEIGHT;
|
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(
|
|
||||||
Text.translatable("gui.done"),
|
|
||||||
button -> this.client.setScreen(parent)
|
|
||||||
).dimensions(this.width / 2 - 100, this.height - 27, 200, 20).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
this.renderBackground(context, mouseX, mouseY, delta);
|
|
||||||
|
|
||||||
int titleX = (this.width / 2) - (this.textRenderer.getWidth(this.title) / 2);
|
|
||||||
context.drawText(this.textRenderer, this.title, titleX, 15, 0xFFFFFF, true);
|
|
||||||
|
|
||||||
int listWidth = this.width - (SIDE_PADDING * 2);
|
|
||||||
int listHeight = this.height - TOP_PADDING - BOTTOM_PADDING;
|
|
||||||
int listLeft = SIDE_PADDING;
|
|
||||||
int listTop = TOP_PADDING;
|
|
||||||
|
|
||||||
context.drawBorder(listLeft, listTop, listLeft + listWidth, listTop + listHeight, 0x00000000);
|
|
||||||
|
|
||||||
context.enableScissor(
|
|
||||||
listLeft,
|
|
||||||
listTop,
|
|
||||||
listLeft + listWidth,
|
|
||||||
listTop + listHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < options.size(); i++) {
|
|
||||||
ConfigOption<?> option = options.get(i);
|
|
||||||
option.y = listTop + (i * OPTION_HEIGHT) - (int) scrollPosition;
|
|
||||||
|
|
||||||
if (option instanceof NumericConfigOption) {
|
|
||||||
((NumericConfigOption<?>) option).updateTextFieldPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.y < listTop + listHeight && option.y + OPTION_HEIGHT > listTop) {
|
|
||||||
option.render(context, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.disableScissor();
|
|
||||||
|
|
||||||
if (contentHeight > listHeight) {
|
|
||||||
int scrollBarHeight = Math.max(20, (int) ((float) listHeight / (float) contentHeight * listHeight));
|
|
||||||
int scrollBarY = listTop + (int) ((scrollPosition / (contentHeight - listHeight)) * (listHeight - scrollBarHeight));
|
|
||||||
|
|
||||||
context.fill(
|
|
||||||
listLeft + listWidth + 2,
|
|
||||||
listTop,
|
|
||||||
listLeft + listWidth + 2 + SCROLL_BAR_WIDTH,
|
|
||||||
listTop + listHeight,
|
|
||||||
0xFF404040
|
|
||||||
);
|
|
||||||
|
|
||||||
context.fill(
|
|
||||||
listLeft + listWidth + 2,
|
|
||||||
scrollBarY,
|
|
||||||
listLeft + listWidth + 2 + SCROLL_BAR_WIDTH,
|
|
||||||
scrollBarY + scrollBarHeight,
|
|
||||||
scrolling ? 0xFFAAAAAA : 0xFF808080
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
||||||
int listWidth = this.width - (SIDE_PADDING * 2);
|
|
||||||
int listHeight = this.height - TOP_PADDING - BOTTOM_PADDING;
|
|
||||||
int listLeft = SIDE_PADDING;
|
|
||||||
int listTop = TOP_PADDING;
|
|
||||||
|
|
||||||
boolean handledByTextField = false;
|
|
||||||
|
|
||||||
activeTextField = null;
|
|
||||||
|
|
||||||
if (mouseX >= listLeft &&
|
|
||||||
mouseX <= listLeft + listWidth &&
|
|
||||||
mouseY >= listTop &&
|
|
||||||
mouseY <= listTop + listHeight) {
|
|
||||||
|
|
||||||
for (ConfigOption<?> option : options) {
|
|
||||||
if (option instanceof NumericConfigOption<?> numOption &&
|
|
||||||
option.y >= listTop &&
|
|
||||||
option.y + OPTION_HEIGHT <= listTop + listHeight) {
|
|
||||||
|
|
||||||
TextFieldWidget textField = numOption.getTextField();
|
|
||||||
|
|
||||||
if (textField.isMouseOver(mouseX, mouseY)) {
|
|
||||||
textField.setFocused(true);
|
|
||||||
activeTextField = textField;
|
|
||||||
handledByTextField = true;
|
|
||||||
|
|
||||||
for (ConfigOption<?> otherOption : options) {
|
|
||||||
if (otherOption instanceof NumericConfigOption && otherOption != option) {
|
|
||||||
((NumericConfigOption<?>) otherOption).getTextField().setFocused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
textField.setFocused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentHeight > listHeight &&
|
|
||||||
mouseX >= listLeft + listWidth + 2 &&
|
|
||||||
mouseX <= listLeft + listWidth + 2 + SCROLL_BAR_WIDTH &&
|
|
||||||
mouseY >= listTop &&
|
|
||||||
mouseY <= listTop + listHeight) {
|
|
||||||
|
|
||||||
scrolling = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if clicked on an option
|
|
||||||
if (mouseX >= listLeft &&
|
|
||||||
mouseX <= listLeft + listWidth &&
|
|
||||||
mouseY >= listTop &&
|
|
||||||
mouseY <= listTop + listHeight) {
|
|
||||||
|
|
||||||
for (ConfigOption<?> option : options) {
|
|
||||||
if (option.isMouseOver(mouseX, mouseY) && option.y >= listTop && option.y + OPTION_HEIGHT <= listTop + listHeight) {
|
|
||||||
option.onClick(mouseX, mouseY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.mouseClicked(mouseX, mouseY, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
|
||||||
if (activeTextField != null && activeTextField.isFocused()) {
|
|
||||||
return activeTextField.keyPressed(keyCode, scanCode, modifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean charTyped(char chr, int modifiers) {
|
|
||||||
if (activeTextField != null && activeTextField.isFocused()) {
|
|
||||||
return activeTextField.charTyped(chr, modifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.charTyped(chr, modifiers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
|
||||||
scrolling = false;
|
|
||||||
return super.mouseReleased(mouseX, mouseY, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
|
||||||
int listHeight = this.height - TOP_PADDING - BOTTOM_PADDING;
|
|
||||||
int listTop = TOP_PADDING;
|
|
||||||
|
|
||||||
if (scrolling && contentHeight > listHeight) {
|
|
||||||
float scrollAmount = (float) deltaY / (listHeight - Math.max(20, (int) ((float) listHeight / (float) contentHeight * listHeight)));
|
|
||||||
float maxScroll = contentHeight - listHeight;
|
|
||||||
|
|
||||||
scrollPosition = MathHelper.clamp(scrollPosition + scrollAmount * maxScroll, 0.0F, maxScroll);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
|
|
||||||
int listWidth = this.width - (SIDE_PADDING * 2);
|
|
||||||
int listHeight = this.height - TOP_PADDING - BOTTOM_PADDING;
|
|
||||||
int listLeft = SIDE_PADDING;
|
|
||||||
int listTop = TOP_PADDING;
|
|
||||||
|
|
||||||
if (mouseX >= listLeft &&
|
|
||||||
mouseX <= listLeft + listWidth &&
|
|
||||||
mouseY >= listTop &&
|
|
||||||
mouseY <= listTop + listHeight &&
|
|
||||||
contentHeight > listHeight) {
|
|
||||||
|
|
||||||
float maxScroll = contentHeight - listHeight;
|
|
||||||
scrollPosition = MathHelper.clamp(scrollPosition - (float) verticalAmount * 10, 0.0F, maxScroll);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatFieldName(String rawName) {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
char[] chars = rawName.toCharArray();
|
|
||||||
result.append(Character.toUpperCase(chars[0]));
|
|
||||||
for (int i = 1; i < chars.length; i++) {
|
|
||||||
if (Character.isUpperCase(chars[i])) {
|
|
||||||
result.append(' ');
|
|
||||||
}
|
|
||||||
result.append(chars[i]);
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all config options
|
|
||||||
*/
|
|
||||||
private abstract class ConfigOption<T> {
|
|
||||||
protected final String name;
|
|
||||||
protected final Field field;
|
|
||||||
protected final int width;
|
|
||||||
protected T value;
|
|
||||||
protected int x;
|
|
||||||
protected int y;
|
|
||||||
|
|
||||||
public ConfigOption(String name, Field field, T initialValue, int x, int y, int width) {
|
|
||||||
this.name = name;
|
|
||||||
this.field = field;
|
|
||||||
this.value = initialValue;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void render(DrawContext context, int mouseX, int mouseY, float delta);
|
|
||||||
|
|
||||||
public abstract void onClick(double mouseX, double mouseY);
|
|
||||||
|
|
||||||
public boolean isMouseOver(double mouseX, double mouseY) {
|
|
||||||
return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + OPTION_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void saveValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation for boolean config options
|
|
||||||
*/
|
|
||||||
private class BooleanConfigOption extends ConfigOption<Boolean> {
|
|
||||||
private static final int BUTTON_WIDTH = 40;
|
|
||||||
private static final int BUTTON_HEIGHT = 20;
|
|
||||||
|
|
||||||
public BooleanConfigOption(String name, Field field, Boolean initialValue, int x, int y, int width) {
|
|
||||||
super(name, field, initialValue, x, y, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
context.drawText(textRenderer, name, x + 5, y + (OPTION_HEIGHT - 8) / 2, 0xFFFFFF, true);
|
|
||||||
|
|
||||||
int buttonX = x + width - BUTTON_WIDTH - 5;
|
|
||||||
int buttonY = y + (OPTION_HEIGHT - BUTTON_HEIGHT) / 2;
|
|
||||||
|
|
||||||
boolean hovered = isButtonHovered(mouseX, mouseY);
|
|
||||||
int buttonColor = hovered ? 0xFF404040 : 0xFF303030;
|
|
||||||
int buttonBorder = hovered ? 0xFFCCCCCC : 0xFF808080;
|
|
||||||
|
|
||||||
context.fill(buttonX, buttonY, buttonX + BUTTON_WIDTH, buttonY + BUTTON_HEIGHT, buttonBorder);
|
|
||||||
context.fill(buttonX + 1, buttonY + 1, buttonX + BUTTON_WIDTH - 1, buttonY + BUTTON_HEIGHT - 1, buttonColor);
|
|
||||||
|
|
||||||
String buttonText = value ? "true" : "false";
|
|
||||||
int textColor = value ? 0x00be00 : 0xbe0000;
|
|
||||||
int textWidth = textRenderer.getWidth(buttonText);
|
|
||||||
context.drawText(
|
|
||||||
textRenderer,
|
|
||||||
buttonText,
|
|
||||||
buttonX + (BUTTON_WIDTH - textWidth) / 2,
|
|
||||||
buttonY + (BUTTON_HEIGHT - 8) / 2,
|
|
||||||
textColor,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isButtonHovered(double mouseX, double mouseY) {
|
|
||||||
int buttonX = x + width - BUTTON_WIDTH - 5;
|
|
||||||
int buttonY = y + (OPTION_HEIGHT - BUTTON_HEIGHT) / 2;
|
|
||||||
return mouseX >= buttonX && mouseX <= buttonX + BUTTON_WIDTH &&
|
|
||||||
mouseY >= buttonY && mouseY <= buttonY + BUTTON_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(double mouseX, double mouseY) {
|
|
||||||
if (isButtonHovered(mouseX, mouseY)) {
|
|
||||||
value = !value;
|
|
||||||
saveValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void saveValue() {
|
|
||||||
try {
|
|
||||||
// Update config and save
|
|
||||||
field.setBoolean(ClientConfigManager.getConfig(), value);
|
|
||||||
ClientConfigManager.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation for String config options
|
|
||||||
*/
|
|
||||||
private class StringConfigOption extends ConfigOption<String> {
|
|
||||||
private static final int FIELD_WIDTH = 60;
|
|
||||||
private static final int FIELD_HEIGHT = 16;
|
|
||||||
protected TextFieldWidget textField;
|
|
||||||
|
|
||||||
public StringConfigOption(String name, Field field, String initialValue, int x, int y, int width) {
|
|
||||||
super(name, field, initialValue, x, y, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createTextField(net.minecraft.client.MinecraftClient client) {
|
|
||||||
int fieldX = x + width - FIELD_WIDTH - 5;
|
|
||||||
int fieldY = y + (OPTION_HEIGHT - FIELD_HEIGHT) / 2;
|
|
||||||
|
|
||||||
textField = new TextFieldWidget(
|
|
||||||
textRenderer,
|
|
||||||
fieldX,
|
|
||||||
fieldY,
|
|
||||||
FIELD_WIDTH,
|
|
||||||
FIELD_HEIGHT,
|
|
||||||
Text.literal("")
|
|
||||||
);
|
|
||||||
|
|
||||||
textField.setText(value);
|
|
||||||
textField.setMaxLength(10);
|
|
||||||
textField.setChangedListener(this::onTextChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextFieldWidget getTextField() {
|
|
||||||
return textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateTextFieldPosition() {
|
|
||||||
if (textField != null) {
|
|
||||||
int fieldX = x + width - FIELD_WIDTH - 5;
|
|
||||||
int fieldY = y + (OPTION_HEIGHT - FIELD_HEIGHT) / 2;
|
|
||||||
textField.setX(fieldX);
|
|
||||||
textField.setY(fieldY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
context.drawText(textRenderer, name, x + 5, y + (OPTION_HEIGHT - 8) / 2, 0xFFFFFF, true);
|
|
||||||
|
|
||||||
if (textField != null) {
|
|
||||||
textField.render(context, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(double mouseX, double mouseY) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTextChanged(String text) {
|
|
||||||
try {
|
|
||||||
value = text;
|
|
||||||
saveValue();
|
|
||||||
} catch (NumberFormatException e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void saveValue() {
|
|
||||||
try {
|
|
||||||
field.set(ClientConfigManager.getConfig(), value);
|
|
||||||
ClientConfigManager.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for numeric config options (int and float)
|
|
||||||
*/
|
|
||||||
private abstract class NumericConfigOption<T extends Number> extends ConfigOption<T> {
|
|
||||||
private static final int FIELD_WIDTH = 60;
|
|
||||||
private static final int FIELD_HEIGHT = 16;
|
|
||||||
protected TextFieldWidget textField;
|
|
||||||
|
|
||||||
public NumericConfigOption(String name, Field field, T initialValue, int x, int y, int width) {
|
|
||||||
super(name, field, initialValue, x, y, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createTextField(net.minecraft.client.MinecraftClient client) {
|
|
||||||
int fieldX = x + width - FIELD_WIDTH - 5;
|
|
||||||
int fieldY = y + (OPTION_HEIGHT - FIELD_HEIGHT) / 2;
|
|
||||||
|
|
||||||
textField = new TextFieldWidget(
|
|
||||||
textRenderer,
|
|
||||||
fieldX,
|
|
||||||
fieldY,
|
|
||||||
FIELD_WIDTH,
|
|
||||||
FIELD_HEIGHT,
|
|
||||||
Text.literal("")
|
|
||||||
);
|
|
||||||
|
|
||||||
textField.setText(value.toString());
|
|
||||||
textField.setMaxLength(10);
|
|
||||||
textField.setChangedListener(this::onTextChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextFieldWidget getTextField() {
|
|
||||||
return textField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateTextFieldPosition() {
|
|
||||||
if (textField != null) {
|
|
||||||
int fieldX = x + width - FIELD_WIDTH - 5;
|
|
||||||
int fieldY = y + (OPTION_HEIGHT - FIELD_HEIGHT) / 2;
|
|
||||||
textField.setX(fieldX);
|
|
||||||
textField.setY(fieldY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
context.drawText(textRenderer, name, x + 5, y + (OPTION_HEIGHT - 8) / 2, 0xFFFFFF, true);
|
|
||||||
|
|
||||||
if (textField != null) {
|
|
||||||
textField.render(context, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(double mouseX, double mouseY) {}
|
|
||||||
|
|
||||||
protected abstract void onTextChanged(String text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation for integer config options
|
|
||||||
*/
|
|
||||||
private class IntegerConfigOption extends NumericConfigOption<Integer> {
|
|
||||||
public IntegerConfigOption(String name, Field field, Integer initialValue, int x, int y, int width) {
|
|
||||||
super(name, field, initialValue, x, y, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(String text) {
|
|
||||||
try {
|
|
||||||
value = Integer.parseInt(text);
|
|
||||||
saveValue();
|
|
||||||
} catch (NumberFormatException e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void saveValue() {
|
|
||||||
try {
|
|
||||||
field.setInt(ClientConfigManager.getConfig(), value);
|
|
||||||
ClientConfigManager.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation for float config options
|
|
||||||
*/
|
|
||||||
private class FloatConfigOption extends NumericConfigOption<Float> {
|
|
||||||
public FloatConfigOption(String name, Field field, Float initialValue, int x, int y, int width) {
|
|
||||||
super(name, field, initialValue, x, y, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(String text) {
|
|
||||||
try {
|
|
||||||
value = Float.parseFloat(text);
|
|
||||||
saveValue();
|
|
||||||
} catch (NumberFormatException e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void saveValue() {
|
|
||||||
try {
|
|
||||||
field.setFloat(ClientConfigManager.getConfig(), value);
|
|
||||||
ClientConfigManager.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -10,6 +10,6 @@ public class ModMenuIntegration implements ModMenuApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||||
return ConfigScreen::new;
|
return YACLConfigScreen::createConfigScreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,231 @@
|
|||||||
|
package wtf.hak.survivalfabric.modmenu;
|
||||||
|
|
||||||
|
import dev.isxander.yacl3.api.*;
|
||||||
|
import dev.isxander.yacl3.api.controller.*;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
|
||||||
|
|
||||||
|
public class YACLConfigScreen {
|
||||||
|
|
||||||
|
public static Screen createConfigScreen(Screen parent) {
|
||||||
|
return YetAnotherConfigLib.createBuilder()
|
||||||
|
.title(Text.literal("Survival Fabric Configuration").formatted(Formatting.BOLD))
|
||||||
|
.category(createMainCategory())
|
||||||
|
.save(ClientConfigManager::save)
|
||||||
|
.build()
|
||||||
|
.generateScreen(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigCategory createMainCategory() {
|
||||||
|
ConfigCategory.Builder categoryBuilder = ConfigCategory.createBuilder()
|
||||||
|
.name(Text.literal("General Settings").formatted(Formatting.YELLOW))
|
||||||
|
.tooltip(Text.literal("Common configuration options for Survival Fabric"));
|
||||||
|
|
||||||
|
// Create groups for different types of options
|
||||||
|
OptionGroup.Builder renderingGroup = OptionGroup.createBuilder()
|
||||||
|
.name(Text.literal("Rendering Options"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Options that control fog rendering and visual effects")))
|
||||||
|
.collapsed(false);
|
||||||
|
|
||||||
|
OptionGroup.Builder gameplayGroup = OptionGroup.createBuilder()
|
||||||
|
.name(Text.literal("Gameplay Options"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Options that affect gameplay mechanics")))
|
||||||
|
.collapsed(false);
|
||||||
|
|
||||||
|
OptionGroup.Builder cameraGroup = OptionGroup.createBuilder()
|
||||||
|
.name(Text.literal("Camera & Zoom"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Camera and zoom-related settings")))
|
||||||
|
.collapsed(false);
|
||||||
|
|
||||||
|
// === RENDERING OPTIONS ===
|
||||||
|
|
||||||
|
// Nether Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render Nether Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog rendering in the Nether dimension")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderNetherFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderNetherFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Overworld Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render Overworld Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog rendering in the Overworld dimension")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderOverworldFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderOverworldFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// End Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render End Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog rendering in the End dimension")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderEndFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderEndFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Lava Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render Lava Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog when submerged in lava")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderLavaFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderLavaFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Water Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render Water Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog when submerged in water")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderWaterFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderWaterFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Snow Fog
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Render Snow Fog"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable or disable fog during snow weather")))
|
||||||
|
.binding(
|
||||||
|
false, // default
|
||||||
|
() -> ClientConfigManager.getConfig().renderSnowFog,
|
||||||
|
newValue -> ClientConfigManager.getConfig().renderSnowFog = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Remove Darkness Effect
|
||||||
|
renderingGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Remove Darkness Effect"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Remove the darkness effect applied by the Warden and sculk shriekers")))
|
||||||
|
.binding(
|
||||||
|
true, // default
|
||||||
|
() -> ClientConfigManager.getConfig().removeDarknessEffect,
|
||||||
|
newValue -> ClientConfigManager.getConfig().removeDarknessEffect = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// === GAMEPLAY OPTIONS ===
|
||||||
|
|
||||||
|
// Lock Teleport Head Movement
|
||||||
|
gameplayGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Lock Teleport Head Movement"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Prevent head movement changes during teleportation")))
|
||||||
|
.binding(
|
||||||
|
true, // default
|
||||||
|
() -> ClientConfigManager.getConfig().lockTeleportHeadMovement,
|
||||||
|
newValue -> ClientConfigManager.getConfig().lockTeleportHeadMovement = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Manipulate Block Entity Distance
|
||||||
|
gameplayGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Manipulate Block Entity Distance"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable custom block entity rendering distance")))
|
||||||
|
.binding(
|
||||||
|
true, // default
|
||||||
|
() -> ClientConfigManager.getConfig().manipulateBlockEntityDistance,
|
||||||
|
newValue -> ClientConfigManager.getConfig().manipulateBlockEntityDistance = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Block Entity Range
|
||||||
|
gameplayGroup.option(Option.<Integer>createBuilder()
|
||||||
|
.name(Text.literal("Block Entity Range"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Maximum distance for block entity rendering (in blocks)")))
|
||||||
|
.binding(
|
||||||
|
512, // default
|
||||||
|
() -> ClientConfigManager.getConfig().blockEntityRange,
|
||||||
|
newValue -> ClientConfigManager.getConfig().blockEntityRange = newValue
|
||||||
|
)
|
||||||
|
.controller(opt -> IntegerSliderControllerBuilder.create(opt)
|
||||||
|
.range(16, 2048)
|
||||||
|
.step(16)
|
||||||
|
.formatValue(value -> Text.literal(value + " blocks")))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// === CAMERA & ZOOM OPTIONS ===
|
||||||
|
|
||||||
|
// Smooth Camera
|
||||||
|
cameraGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Smooth Camera"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Enable smooth camera transitions")))
|
||||||
|
.binding(
|
||||||
|
true, // default
|
||||||
|
() -> ClientConfigManager.getConfig().smoothCamera,
|
||||||
|
newValue -> ClientConfigManager.getConfig().smoothCamera = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Initial Zoom
|
||||||
|
cameraGroup.option(Option.<Float>createBuilder()
|
||||||
|
.name(Text.literal("Initial Zoom Level"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Default zoom level when activating zoom")))
|
||||||
|
.binding(
|
||||||
|
20f, // default
|
||||||
|
() -> ClientConfigManager.getConfig().initialZoom,
|
||||||
|
newValue -> ClientConfigManager.getConfig().initialZoom = newValue
|
||||||
|
)
|
||||||
|
.controller(opt -> FloatSliderControllerBuilder.create(opt)
|
||||||
|
.range(1.0f, 50.0f)
|
||||||
|
.step(0.5f)
|
||||||
|
.formatValue(value -> Text.literal(String.format("%.1fx", value))))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Scroll to Zoom
|
||||||
|
cameraGroup.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Scroll to Zoom"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Allow using mouse wheel to adjust zoom level")))
|
||||||
|
.binding(
|
||||||
|
true, // default
|
||||||
|
() -> ClientConfigManager.getConfig().scrollToZoom,
|
||||||
|
newValue -> ClientConfigManager.getConfig().scrollToZoom = newValue
|
||||||
|
)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Zoom Step
|
||||||
|
cameraGroup.option(Option.<Float>createBuilder()
|
||||||
|
.name(Text.literal("Zoom Step"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Amount of zoom change per scroll wheel step")))
|
||||||
|
.binding(
|
||||||
|
2.5f, // default
|
||||||
|
() -> ClientConfigManager.getConfig().zoomStep,
|
||||||
|
newValue -> ClientConfigManager.getConfig().zoomStep = newValue
|
||||||
|
)
|
||||||
|
.controller(opt -> FloatSliderControllerBuilder.create(opt)
|
||||||
|
.range(0.1f, 10.0f)
|
||||||
|
.step(0.1f)
|
||||||
|
.formatValue(value -> Text.literal(String.format("%.1f", value))))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
return categoryBuilder
|
||||||
|
.group(renderingGroup.build())
|
||||||
|
.group(gameplayGroup.build())
|
||||||
|
.group(cameraGroup.build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,13 +3,17 @@
|
|||||||
"package": "wtf.hak.survivalfabric.mixin.client",
|
"package": "wtf.hak.survivalfabric.mixin.client",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"client": [
|
"client": [
|
||||||
"BackgroundRendererMixin",
|
|
||||||
"BlockEntityRendererMixin",
|
"BlockEntityRendererMixin",
|
||||||
"EntityMixin",
|
"EntityMixin",
|
||||||
|
"FogMixins$DimensionOrBossFogModifierMixin",
|
||||||
|
"FogMixins$LavaFogModifierMixin",
|
||||||
|
"FogMixins$PowderSnowFogModifierMixin",
|
||||||
|
"FogMixins$WaterFogModifierMixin",
|
||||||
"GameRendererMixin",
|
"GameRendererMixin",
|
||||||
"MouseMixin"
|
"MouseMixin"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
}
|
},
|
||||||
|
"mixins": []
|
||||||
}
|
}
|
@@ -27,8 +27,8 @@ public class SlimeChunkCommand {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity();
|
ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity();
|
||||||
Chunk chunk = p.getServerWorld().getChunk(p.getBlockPos());
|
Chunk chunk = p.getWorld().getChunk(p.getBlockPos());
|
||||||
Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getServerWorld().getSeed(), 987234911L);
|
Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getWorld().getSeed(), 987234911L);
|
||||||
if (slimeRandom.nextInt(10) == 0) {
|
if (slimeRandom.nextInt(10) == 0) {
|
||||||
p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage));
|
p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage));
|
||||||
} else
|
} else
|
||||||
|
@@ -45,7 +45,7 @@ public class SpectatorCommand {
|
|||||||
.getZ(), player
|
.getZ(), player
|
||||||
.getYaw(), player
|
.getYaw(), player
|
||||||
.getPitch(), player
|
.getPitch(), player
|
||||||
.getServerWorld()));
|
.getWorld()));
|
||||||
player.changeGameMode(GameMode.SPECTATOR);
|
player.changeGameMode(GameMode.SPECTATOR);
|
||||||
PacketUtils.updateListNames(player);
|
PacketUtils.updateListNames(player);
|
||||||
}
|
}
|
||||||
|
@@ -6,11 +6,8 @@ import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
|||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.block.EnderChestBlock;
|
import net.minecraft.block.EnderChestBlock;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.inventory.Inventories;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.nbt.NbtSizeTracker;
|
|
||||||
import net.minecraft.screen.GenericContainerScreenHandler;
|
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@@ -24,6 +21,7 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
|
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
|
||||||
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
|
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
|
||||||
@@ -38,7 +36,23 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
|
|||||||
File inventoryFile = getFile(server);
|
File inventoryFile = getFile(server);
|
||||||
NbtCompound nbt = new NbtCompound();
|
NbtCompound nbt = new NbtCompound();
|
||||||
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
|
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
|
||||||
Inventories.writeNbt(nbt, sharedInventory.getList(inventoryItemStacks), server.getRegistryManager());
|
inventoryItemStacks = sharedInventory.getList(inventoryItemStacks);
|
||||||
|
|
||||||
|
NbtList itemsList = new NbtList();
|
||||||
|
for (int i = 0; i < inventoryItemStacks.size(); i++) {
|
||||||
|
ItemStack stack = inventoryItemStacks.get(i);
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
NbtCompound itemNbt = new NbtCompound();
|
||||||
|
itemNbt.putInt("Slot", i);
|
||||||
|
|
||||||
|
itemNbt.copyFromCodec(ItemStack.MAP_CODEC,
|
||||||
|
server.getRegistryManager().getOps(NbtOps.INSTANCE), stack);
|
||||||
|
|
||||||
|
itemsList.add(itemNbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nbt.put("Items", itemsList);
|
||||||
|
|
||||||
try (FileOutputStream inventoryFileOutputStream = new FileOutputStream(inventoryFile);
|
try (FileOutputStream inventoryFileOutputStream = new FileOutputStream(inventoryFile);
|
||||||
DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) {
|
DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) {
|
||||||
inventoryFile.createNewFile();
|
inventoryFile.createNewFile();
|
||||||
@@ -79,6 +93,7 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
|
|||||||
return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile();
|
return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void onServerStarted(MinecraftServer server) {
|
public void onServerStarted(MinecraftServer server) {
|
||||||
File inventoryFile = getFile(server);
|
File inventoryFile = getFile(server);
|
||||||
if (inventoryFile.exists()) {
|
if (inventoryFile.exists()) {
|
||||||
@@ -86,7 +101,22 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
|
|||||||
DataInputStream inventoryFileDataInput = new DataInputStream(inventoryFileInputStream)) {
|
DataInputStream inventoryFileDataInput = new DataInputStream(inventoryFileInputStream)) {
|
||||||
NbtCompound nbt = NbtIo.readCompressed(inventoryFileDataInput, NbtSizeTracker.ofUnlimitedBytes());
|
NbtCompound nbt = NbtIo.readCompressed(inventoryFileDataInput, NbtSizeTracker.ofUnlimitedBytes());
|
||||||
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
|
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
|
||||||
Inventories.readNbt(nbt, inventoryItemStacks, server.getRegistryManager());
|
|
||||||
|
// Load each item using the new codec system
|
||||||
|
if (nbt.contains("Items")) {
|
||||||
|
NbtList itemsList = nbt.getList("Items").get();
|
||||||
|
for (int i = 0; i < itemsList.size(); i++) {
|
||||||
|
NbtCompound itemNbt = itemsList.getCompound(i).get();
|
||||||
|
int slot = itemNbt.getInt("Slot").get();
|
||||||
|
if (slot >= 0 && slot < inventoryItemStacks.size()) {
|
||||||
|
// Use ItemStack's codec for deserialization
|
||||||
|
Optional<ItemStack> stackOpt = itemNbt.decode(ItemStack.MAP_CODEC,
|
||||||
|
server.getRegistryManager().getOps(NbtOps.INSTANCE));
|
||||||
|
stackOpt.ifPresent(stack -> inventoryItemStacks.set(slot, stack));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sharedInventory = new SharedInventory(inventoryItemStacks);
|
sharedInventory = new SharedInventory(inventoryItemStacks);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Error while loading Shared Ender Chest: " + e);
|
LOGGER.error("Error while loading Shared Ender Chest: " + e);
|
||||||
|
@@ -19,7 +19,7 @@ public class ServerPlayerEntityMixin {
|
|||||||
private void changePlayerListName(CallbackInfoReturnable<Text> cir) {
|
private void changePlayerListName(CallbackInfoReturnable<Text> cir) {
|
||||||
if (ConfigManager.getConfig().dimensionIndicatorEnabled) {
|
if (ConfigManager.getConfig().dimensionIndicatorEnabled) {
|
||||||
ServerPlayerEntity p = (ServerPlayerEntity) (Object) this;
|
ServerPlayerEntity p = (ServerPlayerEntity) (Object) this;
|
||||||
String world = p.getServerWorld().getRegistryKey().getValue().toTranslationKey();
|
String world = p.getWorld().getRegistryKey().getValue().toTranslationKey();
|
||||||
String finalName;
|
String finalName;
|
||||||
if (!SpectatorCommand.spectating.containsKey(p)) {
|
if (!SpectatorCommand.spectating.containsKey(p)) {
|
||||||
finalName = switch (world) {
|
finalName = switch (world) {
|
||||||
|
@@ -38,9 +38,10 @@
|
|||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.16.10",
|
"fabricloader": ">=0.16.10",
|
||||||
"minecraft": "~1.21.5",
|
"minecraft": "~1.21.6",
|
||||||
"java": ">=21",
|
"java": ">=21",
|
||||||
"fabric-api": "*"
|
"fabric-api": "*",
|
||||||
|
"yet_another_config_lib_v3": ">=3.7.0"
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"modmenu": "*"
|
"modmenu": "*"
|
||||||
|
Reference in New Issue
Block a user