From 7b540521dc2b7f3d25a85e891bd5947ece44d4dd Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Sun, 29 Jun 2025 20:43:51 +0200 Subject: [PATCH] Update to 1.21.6 --- README.md | 2 +- build.gradle | 5 + gradle.properties | 13 +- .../wtf/hak/survivalfabric/features/Zoom.java | 2 - .../mixin/client/BackgroundRendererMixin.java | 47 -- .../mixin/client/FogMixins.java | 80 +++ .../mixin/client/GameRendererMixin.java | 15 + .../survivalfabric/modmenu/ConfigScreen.java | 599 ------------------ .../modmenu/ModMenuIntegration.java | 2 +- .../modmenu/YACLConfigScreen.java | 231 +++++++ .../survivalfabric.client.mixins.json | 8 +- .../commands/SlimeChunkCommand.java | 4 +- .../commands/SpectatorCommand.java | 2 +- .../sharedenderchest/SharedEnderChest.java | 42 +- .../mixin/ServerPlayerEntityMixin.java | 2 +- src/main/resources/fabric.mod.json | 5 +- 16 files changed, 389 insertions(+), 670 deletions(-) delete mode 100644 src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java create mode 100644 src/client/java/wtf/hak/survivalfabric/mixin/client/FogMixins.java delete mode 100644 src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java create mode 100644 src/client/java/wtf/hak/survivalfabric/modmenu/YACLConfigScreen.java diff --git a/README.md b/README.md index 7de2a2c..19339b4 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Code inspired by Inferis! ## Client Side - [x] Fixed bug with too high zoom step values +- [x] Rewrote config screen using YACL ## Server Side - [x] Telekinesis @@ -83,7 +84,6 @@ Code inspired by Inferis! # To-do ## General -- Rework config system - Store server settings in world folder for better singleplayer use ## Client side diff --git a/build.gradle b/build.gradle index 9160502..e8dd8b8 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,10 @@ repositories { name = "Terraformers" url = "https://maven.terraformersmc.com/" } + maven { + name = "Xander Maven" + url = "https://maven.isxander.dev/releases" + } } loom { @@ -43,6 +47,7 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. 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}") } diff --git a/gradle.properties b/gradle.properties index b74da30..5464a87 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,16 +4,17 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.5 -yarn_mappings=1.21.5+build.1 -loader_version=0.16.10 +minecraft_version=1.21.6 +yarn_mappings=1.21.6+build.1 +loader_version=0.16.14 # Mod Properties -mod_version=1.4.1 +mod_version=1.4.2 maven_group=wtf.hak.survivalfabric archives_base_name=survivalfabric # 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 \ No newline at end of file +modmenu_version=15.0.0-beta.1 \ No newline at end of file diff --git a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java index e86f625..60b5ca7 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java +++ b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java @@ -32,9 +32,7 @@ public class Zoom { MinecraftClient.getInstance().options.smoothCameraEnabled = true; } SHOULD_ZOOM = true; - client.gameRenderer.setRenderHand(false); } else if (!ZOOM_BIND.isPressed() && SHOULD_ZOOM) { - client.gameRenderer.setRenderHand(true); SHOULD_ZOOM = false; ZOOM_STEP = 0; if(getConfig().smoothCamera) { diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java deleted file mode 100644 index 437feac..0000000 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java +++ /dev/null @@ -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 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); - } - } -} diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/FogMixins.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/FogMixins.java new file mode 100644 index 0000000..8bebe13 --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/FogMixins.java @@ -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; + } + } + } +} diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java index db7dd3b..535d3d4 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java @@ -1,8 +1,14 @@ package wtf.hak.survivalfabric.mixin.client; 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 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.RenderTickCounter; +import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import wtf.hak.survivalfabric.features.Zoom; @@ -18,4 +24,13 @@ public class GameRendererMixin { 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 original) { + if(!Zoom.isZooming()) + original.call(tickProgress, sleeping, positionMatrix); + } + } diff --git a/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java b/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java deleted file mode 100644 index 209bd99..0000000 --- a/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java +++ /dev/null @@ -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> 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 { - 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 { - 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 { - 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 extends ConfigOption { - 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 { - 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 { - 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(); - } - } - } -} \ No newline at end of file diff --git a/src/client/java/wtf/hak/survivalfabric/modmenu/ModMenuIntegration.java b/src/client/java/wtf/hak/survivalfabric/modmenu/ModMenuIntegration.java index cb7b2e2..51af3d4 100644 --- a/src/client/java/wtf/hak/survivalfabric/modmenu/ModMenuIntegration.java +++ b/src/client/java/wtf/hak/survivalfabric/modmenu/ModMenuIntegration.java @@ -10,6 +10,6 @@ public class ModMenuIntegration implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { - return ConfigScreen::new; + return YACLConfigScreen::createConfigScreen; } } diff --git a/src/client/java/wtf/hak/survivalfabric/modmenu/YACLConfigScreen.java b/src/client/java/wtf/hak/survivalfabric/modmenu/YACLConfigScreen.java new file mode 100644 index 0000000..cc0120d --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/modmenu/YACLConfigScreen.java @@ -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.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.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.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.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.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.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.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.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.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.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.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.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.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.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(); + } +} \ No newline at end of file diff --git a/src/client/resources/survivalfabric.client.mixins.json b/src/client/resources/survivalfabric.client.mixins.json index 109f1b8..6213c92 100644 --- a/src/client/resources/survivalfabric.client.mixins.json +++ b/src/client/resources/survivalfabric.client.mixins.json @@ -3,13 +3,17 @@ "package": "wtf.hak.survivalfabric.mixin.client", "compatibilityLevel": "JAVA_21", "client": [ - "BackgroundRendererMixin", "BlockEntityRendererMixin", "EntityMixin", + "FogMixins$DimensionOrBossFogModifierMixin", + "FogMixins$LavaFogModifierMixin", + "FogMixins$PowderSnowFogModifierMixin", + "FogMixins$WaterFogModifierMixin", "GameRendererMixin", "MouseMixin" ], "injectors": { "defaultRequire": 1 - } + }, + "mixins": [] } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/commands/SlimeChunkCommand.java b/src/main/java/wtf/hak/survivalfabric/commands/SlimeChunkCommand.java index bd0bc90..128e980 100644 --- a/src/main/java/wtf/hak/survivalfabric/commands/SlimeChunkCommand.java +++ b/src/main/java/wtf/hak/survivalfabric/commands/SlimeChunkCommand.java @@ -27,8 +27,8 @@ public class SlimeChunkCommand { return 0; } ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity(); - Chunk chunk = p.getServerWorld().getChunk(p.getBlockPos()); - Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getServerWorld().getSeed(), 987234911L); + Chunk chunk = p.getWorld().getChunk(p.getBlockPos()); + Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getWorld().getSeed(), 987234911L); if (slimeRandom.nextInt(10) == 0) { p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage)); } else diff --git a/src/main/java/wtf/hak/survivalfabric/commands/SpectatorCommand.java b/src/main/java/wtf/hak/survivalfabric/commands/SpectatorCommand.java index 2e7c758..0a0058e 100644 --- a/src/main/java/wtf/hak/survivalfabric/commands/SpectatorCommand.java +++ b/src/main/java/wtf/hak/survivalfabric/commands/SpectatorCommand.java @@ -45,7 +45,7 @@ public class SpectatorCommand { .getZ(), player .getYaw(), player .getPitch(), player - .getServerWorld())); + .getWorld())); player.changeGameMode(GameMode.SPECTATOR); PacketUtils.updateListNames(player); } diff --git a/src/main/java/wtf/hak/survivalfabric/features/sharedenderchest/SharedEnderChest.java b/src/main/java/wtf/hak/survivalfabric/features/sharedenderchest/SharedEnderChest.java index 2f8fde5..e4d516c 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/sharedenderchest/SharedEnderChest.java +++ b/src/main/java/wtf/hak/survivalfabric/features/sharedenderchest/SharedEnderChest.java @@ -6,11 +6,8 @@ import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.minecraft.block.Blocks; import net.minecraft.block.EnderChestBlock; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.Inventories; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtSizeTracker; +import net.minecraft.nbt.*; import net.minecraft.screen.GenericContainerScreenHandler; import net.minecraft.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.server.MinecraftServer; @@ -24,6 +21,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import java.io.*; +import java.util.Optional; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; @@ -38,7 +36,23 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S File inventoryFile = getFile(server); NbtCompound nbt = new NbtCompound(); DefaultedList 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); DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) { inventoryFile.createNewFile(); @@ -79,6 +93,7 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile(); } + public void onServerStarted(MinecraftServer server) { File inventoryFile = getFile(server); if (inventoryFile.exists()) { @@ -86,7 +101,22 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S DataInputStream inventoryFileDataInput = new DataInputStream(inventoryFileInputStream)) { NbtCompound nbt = NbtIo.readCompressed(inventoryFileDataInput, NbtSizeTracker.ofUnlimitedBytes()); DefaultedList 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 stackOpt = itemNbt.decode(ItemStack.MAP_CODEC, + server.getRegistryManager().getOps(NbtOps.INSTANCE)); + stackOpt.ifPresent(stack -> inventoryItemStacks.set(slot, stack)); + } + } + } + sharedInventory = new SharedInventory(inventoryItemStacks); } catch (Exception e) { LOGGER.error("Error while loading Shared Ender Chest: " + e); diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java index d3bec63..0658184 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java @@ -19,7 +19,7 @@ public class ServerPlayerEntityMixin { private void changePlayerListName(CallbackInfoReturnable cir) { if (ConfigManager.getConfig().dimensionIndicatorEnabled) { ServerPlayerEntity p = (ServerPlayerEntity) (Object) this; - String world = p.getServerWorld().getRegistryKey().getValue().toTranslationKey(); + String world = p.getWorld().getRegistryKey().getValue().toTranslationKey(); String finalName; if (!SpectatorCommand.spectating.containsKey(p)) { finalName = switch (world) { diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 36ad2c7..46beb68 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -38,9 +38,10 @@ ], "depends": { "fabricloader": ">=0.16.10", - "minecraft": "~1.21.5", + "minecraft": "~1.21.6", "java": ">=21", - "fabric-api": "*" + "fabric-api": "*", + "yet_another_config_lib_v3": ">=3.7.0" }, "optional": { "modmenu": "*"