From f51bde3d6e6c7b88b62a00fe267a1454c435d72b Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Sat, 12 Apr 2025 20:53:52 +0200 Subject: [PATCH 01/27] Render Block Entities from further away --- README.md | 11 +++++++++- .../config/client/ClientConfig.java | 2 ++ .../client/BlockEntityRendererMixin.java | 22 +++++++++++++++++++ .../survivalfabric.client.mixins.json | 1 + 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java diff --git a/README.md b/README.md index 03d8767..5e39e8b 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,21 @@ As a challenge I'm trying to make it as user-friendly as possible. - Toggleable - Keybinding for /camera +# Currently working on + +## Client Side + +- [x] Render block entities from a longer range + - [x] Toggleable via GUI + - [x] Configurable value + - [ ] In GUI + # To-do ## General - Rework config system - Store server settings in world folder for better singleplayer use - - Rework Mod Menu integration to be more flexible +- Rework Mod Menu integration to be more flexible ## Server Side - Telekinesis diff --git a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java index d9f5cb5..1e4fb84 100644 --- a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java +++ b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java @@ -11,4 +11,6 @@ public class ClientConfig { public boolean renderSnowFog = false; public boolean removeDarknessEffect = true; public boolean lockTeleportHeadMovement = true; + public boolean alwaysRenderBlockEntities = true; + public int renderBlockEntitiesRange = 512; } diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java new file mode 100644 index 0000000..e238165 --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java @@ -0,0 +1,22 @@ +package wtf.hak.survivalfabric.mixin.client; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import wtf.hak.survivalfabric.config.ConfigManager; +import wtf.hak.survivalfabric.config.client.ClientConfigManager; + +@Mixin(BlockEntityRenderer.class) +public interface BlockEntityRendererMixin { + + @Inject(method = "getRenderDistance", at = @At("HEAD"), cancellable = true) + private void getRenderDistance(CallbackInfoReturnable cir) { + if(ClientConfigManager.getConfig().alwaysRenderBlockEntities) + cir.setReturnValue(ClientConfigManager.getConfig().renderBlockEntitiesRange); + } +} diff --git a/src/client/resources/survivalfabric.client.mixins.json b/src/client/resources/survivalfabric.client.mixins.json index 6e215a6..b5906b0 100644 --- a/src/client/resources/survivalfabric.client.mixins.json +++ b/src/client/resources/survivalfabric.client.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_21", "client": [ "BackgroundRendererMixin", + "BlockEntityRendererMixin", "EntityMixin" ], "injectors": { From c25790902ce014363abeb0eb31b8d94905b5bfe4 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Sat, 12 Apr 2025 23:48:47 +0200 Subject: [PATCH 02/27] Revamped Config Screen --- README.md | 9 +- .../config/client/ClientConfig.java | 4 +- .../client/BlockEntityRendererMixin.java | 7 +- .../survivalfabric/modmenu/ConfigScreen.java | 578 +++++++++++++++++- 4 files changed, 556 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5e39e8b..a7aec3b 100644 --- a/README.md +++ b/README.md @@ -56,14 +56,19 @@ As a challenge I'm trying to make it as user-friendly as possible. - [x] Render block entities from a longer range - [x] Toggleable via GUI - [x] Configurable value - - [ ] In GUI + - [x] In GUI +- [x] Rework Mod Menu integration to be more flexible + - The following types are accepted: + - String + - Boolean + - Float + - Integer # To-do ## General - Rework config system - Store server settings in world folder for better singleplayer use -- Rework Mod Menu integration to be more flexible ## Server Side - Telekinesis diff --git a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java index 1e4fb84..2611e7e 100644 --- a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java +++ b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java @@ -11,6 +11,6 @@ public class ClientConfig { public boolean renderSnowFog = false; public boolean removeDarknessEffect = true; public boolean lockTeleportHeadMovement = true; - public boolean alwaysRenderBlockEntities = true; - public int renderBlockEntitiesRange = 512; + public boolean manipulateBlockEntityDistance = true; + public int blockEntityRange = 512; } diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java index e238165..7654cbd 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java @@ -1,14 +1,11 @@ package wtf.hak.survivalfabric.mixin.client; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; import net.minecraft.client.render.block.entity.BlockEntityRenderer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import wtf.hak.survivalfabric.config.ConfigManager; import wtf.hak.survivalfabric.config.client.ClientConfigManager; @Mixin(BlockEntityRenderer.class) @@ -16,7 +13,7 @@ public interface BlockEntityRendererMixin { @Inject(method = "getRenderDistance", at = @At("HEAD"), cancellable = true) private void getRenderDistance(CallbackInfoReturnable cir) { - if(ClientConfigManager.getConfig().alwaysRenderBlockEntities) - cir.setReturnValue(ClientConfigManager.getConfig().renderBlockEntitiesRange); + if(ClientConfigManager.getConfig().manipulateBlockEntityDistance) + cir.setReturnValue(ClientConfigManager.getConfig().blockEntityRange); } } diff --git a/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java b/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java index 6b4843f..209bd99 100644 --- a/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java +++ b/src/client/java/wtf/hak/survivalfabric/modmenu/ConfigScreen.java @@ -3,15 +3,29 @@ 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")); @@ -20,55 +34,272 @@ public class ConfigScreen extends Screen { @Override protected void init() { - int buttonWidth = 200; - int buttonHeight = 20; - int spacing = 5; - int startY = 40; - int i = 0; + 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()) { - if (field.getType() == boolean.class) { - int y = startY + i * (buttonHeight + spacing); - try { + try { + Class type = field.getType(); + String name = formatFieldName(field.getName()); + + ConfigOption option = null; + if (type == boolean.class) { boolean value = field.getBoolean(ClientConfigManager.getConfig()); - String label = formatFieldName(field.getName()) + ": " + (value ? "ON" : "OFF"); - - ButtonWidget button = ButtonWidget.builder( - Text.literal(label), - b -> { - try { - boolean current = field.getBoolean(ClientConfigManager.getConfig()); - field.setBoolean(ClientConfigManager.getConfig(), !current); - b.setMessage(Text.literal(formatFieldName(field.getName()) + ": " + (!current ? "ON" : "OFF"))); - ClientConfigManager.save(); // Save if needed - } catch (Exception e) { - e.printStackTrace(); - } - } - ).dimensions(this.width / 2 - buttonWidth / 2, y, buttonWidth, buttonHeight).build(); - - this.addDrawableChild(button); - i++; - } catch (Exception e) { - e.printStackTrace(); + 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 - 75, startY + i * (buttonHeight + spacing) + 10, 150, 20).build()); + ).dimensions(this.width / 2 - 100, this.height - 27, 200, 20).build()); } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - context.fill(0, 0, this.width, this.height, 0xC0101010); + this.renderBackground(context, mouseX, mouseY, delta); int titleX = (this.width / 2) - (this.textRenderer.getWidth(this.title) / 2); - context.drawTextWithShadow(this.textRenderer, this.title, titleX, 20, 0xFFFFFF); + context.drawText(this.textRenderer, this.title, titleX, 15, 0xFFFFFF, true); - super.render(context, mouseX, mouseY, delta); + 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) { @@ -84,4 +315,285 @@ public class ConfigScreen extends Screen { 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 From 0dd5907a29500b9927d1108d9c194542e6904965 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Sat, 12 Apr 2025 23:49:27 +0200 Subject: [PATCH 03/27] Updated mod version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 9969eda..b74da30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.21.5+build.1 loader_version=0.16.10 # Mod Properties -mod_version=1.4.0 +mod_version=1.4.1 maven_group=wtf.hak.survivalfabric archives_base_name=survivalfabric From 8498a777355ae30fb31d3c8e39fc773d0be76520 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Sun, 13 Apr 2025 17:51:47 +0200 Subject: [PATCH 04/27] Fixed toggleability for Vein Miner --- src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java index 0af8e4b..6bb8876 100644 --- a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java +++ b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java @@ -32,7 +32,7 @@ public class SurvivalFabric implements ModInitializer { if (getConfig().veinMinerEnabled) { PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> { - if (player instanceof ServerPlayerEntity serverPlayer) { + if (player instanceof ServerPlayerEntity serverPlayer && getConfig().veinMinerEnabled) { return VeinMinerEvents.beforeBlockBreak(world, serverPlayer, pos, state); } else { return true; From f994e8cc11ccc72052956afdb2135da4378caae3 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Tue, 22 Apr 2025 16:33:58 +0200 Subject: [PATCH 05/27] Changed default config --- src/main/java/wtf/hak/survivalfabric/config/Config.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/wtf/hak/survivalfabric/config/Config.java b/src/main/java/wtf/hak/survivalfabric/config/Config.java index 3ab7c72..c53da01 100644 --- a/src/main/java/wtf/hak/survivalfabric/config/Config.java +++ b/src/main/java/wtf/hak/survivalfabric/config/Config.java @@ -29,8 +29,8 @@ public class Config { public boolean sharedEnderChestEnabled = true; public String sharedEnderChestName = "Ender Chest"; public int sharedEnderChestRows = 6; - public boolean sharedEnderChestLimitedAccess = true; - public List sharedEnderChestNames = Lists.newArrayList("AlwaysHAK", "LunaticFox"); + public boolean sharedEnderChestLimitedAccess = false; + public List sharedEnderChestNames = Lists.newArrayList("AlwaysHAK"); public String inSlimeChunkMessage = "§aYou're currently in a slime chunk"; public String notInSlimeChunkMessage = "§cYou're currently not in a slime chunk"; From 49ee0c503fffbbdf3053432f0191e1b0684e3957 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Tue, 22 Apr 2025 16:34:22 +0200 Subject: [PATCH 06/27] Fixed Chat Calc --- .../mixin/PlayerManagerMixin.java | 48 ++--------------- .../hak/survivalfabric/utils/MathUtils.java | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java index b683469..2067bbe 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java @@ -15,6 +15,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.config.ConfigManager; +import wtf.hak.survivalfabric.utils.MathUtils; import java.util.Set; @@ -56,10 +57,10 @@ public abstract class PlayerManagerMixin { String processedMessage = rawMessage; - if (isCalcEnabled) { + if (isCalcEnabled && MathUtils.hasSupportedOperator(rawMessage)) { String expression = rawMessage.endsWith("=") ? rawMessage.substring(0, rawMessage.length() - 1).trim() : rawMessage; try { - String result = String.valueOf(evaluateExpression(expression)); + String result = String.valueOf(MathUtils.evaluateExpression(expression)); StringBuilder sb = new StringBuilder(rawMessage).append("§6"); if (rawMessage.contains(" ")) sb.append(" "); @@ -83,47 +84,4 @@ public abstract class PlayerManagerMixin { } } - - private double evaluateExpression(String expression) { - return evaluate(expression.replaceAll("\\s", ""), new int[]{0}); - } - - private double evaluate(String expr, int[] index) { - double value = parseTerm(expr, index); - while (index[0] < expr.length()) { - char op = expr.charAt(index[0]); - if (op != '+' && op != '-') break; - index[0]++; - double nextTerm = parseTerm(expr, index); - value = (op == '+') ? value + nextTerm : value - nextTerm; - } - return value; - } - - private double parseTerm(String expr, int[] index) { - double value = parseFactor(expr, index); - while (index[0] < expr.length()) { - char op = expr.charAt(index[0]); - if (op != '*' && op != '/') break; - index[0]++; - double nextFactor = parseFactor(expr, index); - value = (op == '*') ? value * nextFactor : value / nextFactor; - } - return value; - } - - private double parseFactor(String expr, int[] index) { - if (expr.charAt(index[0]) == '(') { - index[0]++; - double value = evaluate(expr, index); - index[0]++; // Skip closing ')' - return value; - } - - int start = index[0]; - while (index[0] < expr.length() && (Character.isDigit(expr.charAt(index[0])) || expr.charAt(index[0]) == '.')) { - index[0]++; - } - return Double.parseDouble(expr.substring(start, index[0])); - } } diff --git a/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java b/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java new file mode 100644 index 0000000..b8018c6 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java @@ -0,0 +1,52 @@ +package wtf.hak.survivalfabric.utils; + +public class MathUtils { + + + public static boolean hasSupportedOperator(String msg) { + return msg.contains("+") || msg.contains("-") || msg.contains("*") || msg.contains("/"); + } + + public static double evaluateExpression(String expression) { + return evaluate(expression.replaceAll("\\s", ""), new int[]{0}); + } + + public static double evaluate(String expr, int[] index) { + double value = parseTerm(expr, index); + while (index[0] < expr.length()) { + char op = expr.charAt(index[0]); + if (op != '+' && op != '-') break; + index[0]++; + double nextTerm = parseTerm(expr, index); + value = (op == '+') ? value + nextTerm : value - nextTerm; + } + return value; + } + + public static double parseTerm(String expr, int[] index) { + double value = parseFactor(expr, index); + while (index[0] < expr.length()) { + char op = expr.charAt(index[0]); + if (op != '*' && op != '/') break; + index[0]++; + double nextFactor = parseFactor(expr, index); + value = (op == '*') ? value * nextFactor : value / nextFactor; + } + return value; + } + + public static double parseFactor(String expr, int[] index) { + if (expr.charAt(index[0]) == '(') { + index[0]++; + double value = evaluate(expr, index); + index[0]++; // Skip closing ')' + return value; + } + + int start = index[0]; + while (index[0] < expr.length() && (Character.isDigit(expr.charAt(index[0])) || expr.charAt(index[0]) == '.')) { + index[0]++; + } + return Double.parseDouble(expr.substring(start, index[0])); + } +} From be6472cc79aa2b76afcf8a01511850cca3f02b84 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Tue, 22 Apr 2025 16:34:35 +0200 Subject: [PATCH 07/27] Updated README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7aec3b..7bf0fd9 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ As a challenge I'm trying to make it as user-friendly as possible. - Automatic config adaption (currently booleans only) - Remove darkness effect - Toggleable -- Keybinding for /camera +- Keybinding for /spectator # Currently working on @@ -63,6 +63,8 @@ As a challenge I'm trying to make it as user-friendly as possible. - Boolean - Float - Integer +- [x] Chat Calculator + - [x] Check if operator is present before calculating # To-do @@ -70,6 +72,10 @@ As a challenge I'm trying to make it as user-friendly as possible. - Rework config system - Store server settings in world folder for better singleplayer use +## Client side +- Zoom + - Configurable + ## Server Side - Telekinesis From c7ea93349488d02f13faa75787268d96c2ef8b1b Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Wed, 23 Apr 2025 20:53:15 +0200 Subject: [PATCH 08/27] Added documentation to Mixin classes --- .../hak/survivalfabric/mixin/PlayerManagerMixin.java | 12 ++++++++++++ .../mixin/ServerPlayNetworkHandlerMixin.java | 3 +++ .../mixin/ServerPlayerEntityMixin.java | 3 +++ .../hak/survivalfabric/mixin/ServerWorldMixin.java | 3 +++ 4 files changed, 21 insertions(+) diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java index 2067bbe..d5b6ad6 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/PlayerManagerMixin.java @@ -22,6 +22,9 @@ import java.util.Set; @Mixin(PlayerManager.class) public abstract class PlayerManagerMixin { + /** + * Send join message to joined player + */ @Inject(method = {"onPlayerConnect"}, at = {@At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")}) public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) { if (ConfigManager.getConfig().joinMessageEnabled && !player.getServer().isSingleplayer()) { @@ -30,6 +33,9 @@ public abstract class PlayerManagerMixin { } } + /** + * Modify join message broadcasted to server + */ @ModifyArg(method = {"onPlayerConnect"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")) private Text onPlayerConnect(Text text) { if (ConfigManager.getConfig().joinMessageEnabled) { @@ -39,6 +45,9 @@ public abstract class PlayerManagerMixin { return text; } + /** + * Get player out of specator mode if necessary + */ @Inject(method = {"remove"}, at = {@At("HEAD")}) public void onPlayerLeave(ServerPlayerEntity player, CallbackInfo ci) { if (SpectatorCommand.spectating.containsKey(player)) { @@ -48,6 +57,9 @@ public abstract class PlayerManagerMixin { } } + /** + * Modify chat messages + */ @Inject(method = {"broadcast(Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/network/message/MessageType$Parameters;)V"}, at = {@At("HEAD")}, cancellable = true) private void onBroadcast(SignedMessage message, ServerPlayerEntity sender, MessageType.Parameters parameters, CallbackInfo ci) { if (sender != null) { diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java index 8d2caf9..db19be8 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java @@ -10,6 +10,9 @@ import wtf.hak.survivalfabric.config.ConfigManager; @Mixin(ServerPlayNetworkHandler.class) public abstract class ServerPlayNetworkHandlerMixin { + /** + * Modify quit message + */ @ModifyArg(method = {"cleanUp"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")) private Text quitMessage(Text text) { if (ConfigManager.getConfig().quitMessageEnabled) { diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java index d0110f9..3fb2fb0 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java @@ -12,6 +12,9 @@ import wtf.hak.survivalfabric.config.ConfigManager; @Mixin(ServerPlayerEntity.class) public abstract class ServerPlayerEntityMixin { + /** + * Change player list name if enabled + */ @Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true) private void changePlayerListName(CallbackInfoReturnable cir) { if (ConfigManager.getConfig().dimensionIndicatorEnabled) { diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerWorldMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerWorldMixin.java index d2ea4ae..60bfb28 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerWorldMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerWorldMixin.java @@ -12,6 +12,9 @@ import wtf.hak.survivalfabric.utils.PacketUtils; @Mixin(ServerWorld.class) public class ServerWorldMixin { + /** + * Update List Names if needed + */ @Inject(method = "onDimensionChanged", at = {@At("HEAD")}) public void onDimensionChange(Entity entity, CallbackInfo ci) { if (entity instanceof ServerPlayerEntity) { From 1d3dba1b02a44739112c72f2f9f5f60d3ce09cdb Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Wed, 23 Apr 2025 21:21:10 +0200 Subject: [PATCH 09/27] Changed Mixin from interface(?) to abstract class --- .../survivalfabric/mixin/client/BlockEntityRendererMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java index 7654cbd..3bd8656 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java @@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import wtf.hak.survivalfabric.config.client.ClientConfigManager; @Mixin(BlockEntityRenderer.class) -public interface BlockEntityRendererMixin { +public abstract class BlockEntityRendererMixin { @Inject(method = "getRenderDistance", at = @At("HEAD"), cancellable = true) private void getRenderDistance(CallbackInfoReturnable cir) { From 2c95b5e37435940623d182416016c67b73da4806 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Wed, 23 Apr 2025 21:26:56 +0200 Subject: [PATCH 10/27] Oops --- .../survivalfabric/mixin/client/BlockEntityRendererMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java index 3bd8656..7654cbd 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java @@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import wtf.hak.survivalfabric.config.client.ClientConfigManager; @Mixin(BlockEntityRenderer.class) -public abstract class BlockEntityRendererMixin { +public interface BlockEntityRendererMixin { @Inject(method = "getRenderDistance", at = @At("HEAD"), cancellable = true) private void getRenderDistance(CallbackInfoReturnable cir) { From 963002570d4870c11084f898e8fb3613d3cd2ea8 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Wed, 23 Apr 2025 22:32:13 +0200 Subject: [PATCH 11/27] Added zoom functionality --- .../survivalfabric/SurvivalFabricClient.java | 3 +- .../wtf/hak/survivalfabric/features/Zoom.java | 52 +++++++++++++++++++ .../mixin/client/GameRendererMixin.java | 21 ++++++++ .../mixin/client/MouseMixin.java | 31 +++++++++++ .../survivalfabric.client.mixins.json | 4 +- .../hak/survivalfabric/utils/MathUtils.java | 12 +++++ .../assets/survivalfabric/lang/en_us.json | 3 +- 7 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 src/client/java/wtf/hak/survivalfabric/features/Zoom.java create mode 100644 src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java create mode 100644 src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java diff --git a/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java b/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java index 099d664..26fc1e5 100644 --- a/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java +++ b/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java @@ -5,6 +5,7 @@ import wtf.hak.survivalfabric.config.client.ClientConfigManager; import wtf.hak.survivalfabric.features.AngleViewer; import wtf.hak.survivalfabric.features.RemoveDarknessEffect; import wtf.hak.survivalfabric.features.SFKeyBindings; +import wtf.hak.survivalfabric.features.Zoom; public class SurvivalFabricClient implements ClientModInitializer { @@ -18,6 +19,6 @@ public class SurvivalFabricClient implements ClientModInitializer { AngleViewer.register(); RemoveDarknessEffect.register(); SFKeyBindings.register(); - + Zoom.register(); } } \ 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 new file mode 100644 index 0000000..23b907c --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java @@ -0,0 +1,52 @@ +package wtf.hak.survivalfabric.features; + +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import net.minecraft.text.Text; +import org.lwjgl.glfw.GLFW; +import wtf.hak.survivalfabric.utils.MathUtils; + +public class Zoom { + + private static final KeyBinding ZOOM_BIND = KeyBindingHelper.registerKeyBinding(new KeyBinding("key.survivalfabric.zoom", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_C, "category.survivalfabric.survivalfabric")); + private static final float INITIAL_ZOOM = 20f; + + private static boolean SHOULD_ZOOM = false; + private static float ZOOM_STEP_VALUE = 2.5f; + private static int ZOOM_STEP = 0; + + public static void register() { + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if(ZOOM_BIND.isPressed()) + SHOULD_ZOOM = true; + else { + SHOULD_ZOOM = false; + ZOOM_STEP = 0; + } + }); + } + + public static boolean isZooming() { + return SHOULD_ZOOM; + } + + public static float getZoomFov() { + return INITIAL_ZOOM - -ZOOM_STEP * ZOOM_STEP_VALUE; + } + + public static void modifyStep(int step) { + ZOOM_STEP += step; + + // Clamp the zoom level so the FOV stays within [1, 110] + float zoomFov = getZoomFov(); + if (zoomFov < 1) { + ZOOM_STEP = Math.round((1 - INITIAL_ZOOM) / ZOOM_STEP_VALUE)+1; + } else if (zoomFov > 110) { + ZOOM_STEP = Math.round((110 - INITIAL_ZOOM) / ZOOM_STEP_VALUE); + } + MinecraftClient.getInstance().player.sendMessage(Text.literal(ZOOM_STEP + ""), true); + } +} diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java new file mode 100644 index 0000000..9106213 --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java @@ -0,0 +1,21 @@ +package wtf.hak.survivalfabric.mixin.client; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.client.render.GameRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import wtf.hak.survivalfabric.features.Zoom; + +@Mixin(GameRenderer.class) +public abstract class GameRendererMixin { + + /** + * Modify Zoom FOV + */ + @ModifyReturnValue(method = "getFov", at = @At("RETURN")) + private float modifyFovWithZoom(float fov, @Local(argsOnly = true) float tickDelta) { + return Zoom.isZooming() ? Zoom.getZoomFov() : fov; + } + +} diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java new file mode 100644 index 0000000..6f80828 --- /dev/null +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java @@ -0,0 +1,31 @@ +package wtf.hak.survivalfabric.mixin.client; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.Mouse; +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 wtf.hak.survivalfabric.features.Zoom; + +@Mixin(Mouse.class) +public class MouseMixin { + + /** + * Scrolling listener for zooming + */ + @Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true) + private void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) { + if(Zoom.isZooming()) { + if(MinecraftClient.getInstance().player != null) { + if(vertical>0) + Zoom.modifyStep(-1); + else + Zoom.modifyStep(1); + } + ci.cancel(); + } + } + + +} diff --git a/src/client/resources/survivalfabric.client.mixins.json b/src/client/resources/survivalfabric.client.mixins.json index b5906b0..109f1b8 100644 --- a/src/client/resources/survivalfabric.client.mixins.json +++ b/src/client/resources/survivalfabric.client.mixins.json @@ -5,7 +5,9 @@ "client": [ "BackgroundRendererMixin", "BlockEntityRendererMixin", - "EntityMixin" + "EntityMixin", + "GameRendererMixin", + "MouseMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java b/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java index b8018c6..d5abf0e 100644 --- a/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java +++ b/src/main/java/wtf/hak/survivalfabric/utils/MathUtils.java @@ -3,6 +3,18 @@ package wtf.hak.survivalfabric.utils; public class MathUtils { + public static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + public static float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } + + public static int clamp(int value, int min, int max) { + return Math.max(min, Math.min(max, value)); + } + public static boolean hasSupportedOperator(String msg) { return msg.contains("+") || msg.contains("-") || msg.contains("*") || msg.contains("/"); } diff --git a/src/main/resources/assets/survivalfabric/lang/en_us.json b/src/main/resources/assets/survivalfabric/lang/en_us.json index 92e23d7..03f83b6 100644 --- a/src/main/resources/assets/survivalfabric/lang/en_us.json +++ b/src/main/resources/assets/survivalfabric/lang/en_us.json @@ -17,5 +17,6 @@ "key.survivalfabric.angle14": "204.98 / -41.68", "key.survivalfabric.angle15": "244.97 / -41.71", "category.survivalfabric.survivalfabric": "Survival Fabric", - "key.survivalfabric.camera": "/camera" + "key.survivalfabric.camera": "/camera", + "key.survivalfabric.zoom": "Zoom" } \ No newline at end of file From 8808df1cf13ebf1f8c921155612e776fc10aeadf Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Wed, 23 Apr 2025 22:32:18 +0200 Subject: [PATCH 12/27] Added zoom functionality --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7bf0fd9..8bfde8d 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ As a challenge I'm trying to make it as user-friendly as possible. - Integer - [x] Chat Calculator - [x] Check if operator is present before calculating +- [x] Zoom + - [ ] Configurable # To-do @@ -73,8 +75,6 @@ As a challenge I'm trying to make it as user-friendly as possible. - Store server settings in world folder for better singleplayer use ## Client side -- Zoom - - Configurable ## Server Side - Telekinesis From 4988a91e70438296ea5191606c65b600dc902f27 Mon Sep 17 00:00:00 2001 From: AlwaysHAK Date: Sun, 27 Apr 2025 15:37:14 +0200 Subject: [PATCH 13/27] Fixed mixin class types --- .../survivalfabric/mixin/client/BackgroundRendererMixin.java | 2 +- .../wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java | 2 +- .../hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java | 2 +- .../wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java index 0c42d31..437feac 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BackgroundRendererMixin.java @@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig; @Mixin(value = BackgroundRenderer.class, priority = 910) -public abstract class BackgroundRendererMixin { +public class BackgroundRendererMixin { @Unique private static final Fog EMPTY_FOG = new Fog(-8.0f, 1_000_000.0F, FogShape.CYLINDER, 0, 0, 0, 0); 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 9106213..357cb74 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/GameRendererMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.At; import wtf.hak.survivalfabric.features.Zoom; @Mixin(GameRenderer.class) -public abstract class GameRendererMixin { +public class GameRendererMixin { /** * Modify Zoom FOV diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java index db19be8..b17fe9e 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayNetworkHandlerMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import wtf.hak.survivalfabric.config.ConfigManager; @Mixin(ServerPlayNetworkHandler.class) -public abstract class ServerPlayNetworkHandlerMixin { +public class ServerPlayNetworkHandlerMixin { /** * Modify quit message diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java index 3fb2fb0..d3bec63 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/ServerPlayerEntityMixin.java @@ -10,7 +10,7 @@ import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.config.ConfigManager; @Mixin(ServerPlayerEntity.class) -public abstract class ServerPlayerEntityMixin { +public class ServerPlayerEntityMixin { /** * Change player list name if enabled From 69df0e58506a11f9b54d7b86503b253bd60bf4b2 Mon Sep 17 00:00:00 2001 From: AlwaysHAK Date: Sun, 27 Apr 2025 15:41:38 +0200 Subject: [PATCH 14/27] Cleaned up some code --- .../java/wtf/hak/survivalfabric/features/SFKeyBindings.java | 2 +- src/client/java/wtf/hak/survivalfabric/features/Zoom.java | 5 ++--- .../mixin/client/BlockEntityRendererMixin.java | 2 +- .../wtf/hak/survivalfabric/mixin/client/MouseMixin.java | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java b/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java index 73df70b..6f97569 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java +++ b/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java @@ -12,7 +12,7 @@ public class SFKeyBindings { public static void register() { ClientTickEvents.END_CLIENT_TICK.register(client -> { - if(client.player != null) { + if (client.player != null) { if (CAMERA_BIND.wasPressed()) { client.player.networkHandler.sendChatCommand("camera"); } diff --git a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java index 23b907c..68c2206 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java +++ b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java @@ -7,7 +7,6 @@ import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; import org.lwjgl.glfw.GLFW; -import wtf.hak.survivalfabric.utils.MathUtils; public class Zoom { @@ -20,7 +19,7 @@ public class Zoom { public static void register() { ClientTickEvents.END_CLIENT_TICK.register(client -> { - if(ZOOM_BIND.isPressed()) + if (ZOOM_BIND.isPressed()) SHOULD_ZOOM = true; else { SHOULD_ZOOM = false; @@ -43,7 +42,7 @@ public class Zoom { // Clamp the zoom level so the FOV stays within [1, 110] float zoomFov = getZoomFov(); if (zoomFov < 1) { - ZOOM_STEP = Math.round((1 - INITIAL_ZOOM) / ZOOM_STEP_VALUE)+1; + ZOOM_STEP = Math.round((1 - INITIAL_ZOOM) / ZOOM_STEP_VALUE) + 1; } else if (zoomFov > 110) { ZOOM_STEP = Math.round((110 - INITIAL_ZOOM) / ZOOM_STEP_VALUE); } diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java index 7654cbd..21369b8 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/BlockEntityRendererMixin.java @@ -13,7 +13,7 @@ public interface BlockEntityRendererMixin { @Inject(method = "getRenderDistance", at = @At("HEAD"), cancellable = true) private void getRenderDistance(CallbackInfoReturnable cir) { - if(ClientConfigManager.getConfig().manipulateBlockEntityDistance) + if (ClientConfigManager.getConfig().manipulateBlockEntityDistance) cir.setReturnValue(ClientConfigManager.getConfig().blockEntityRange); } } diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java index 6f80828..283de8e 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java @@ -16,9 +16,9 @@ public class MouseMixin { */ @Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true) private void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) { - if(Zoom.isZooming()) { - if(MinecraftClient.getInstance().player != null) { - if(vertical>0) + if (Zoom.isZooming()) { + if (MinecraftClient.getInstance().player != null) { + if (vertical > 0) Zoom.modifyStep(-1); else Zoom.modifyStep(1); From ee448ae34d637f58c70afee66831b7c4b4d312e1 Mon Sep 17 00:00:00 2001 From: AlwaysHAK Date: Sun, 27 Apr 2025 16:36:36 +0200 Subject: [PATCH 15/27] Removed debug message --- src/client/java/wtf/hak/survivalfabric/features/Zoom.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java index 68c2206..dfc8ec0 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java +++ b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java @@ -46,6 +46,5 @@ public class Zoom { } else if (zoomFov > 110) { ZOOM_STEP = Math.round((110 - INITIAL_ZOOM) / ZOOM_STEP_VALUE); } - MinecraftClient.getInstance().player.sendMessage(Text.literal(ZOOM_STEP + ""), true); } } From ef29652e074579405e2c89b6d2ffdab67409d9b0 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 10:15:29 +0200 Subject: [PATCH 16/27] Made leaves veinmineable using shears --- .../features/veinminer/drills/LeavesDrill.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java index 3b56798..eb614c0 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java @@ -2,6 +2,7 @@ package wtf.hak.survivalfabric.features.veinminer.drills; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.item.Items; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; import net.minecraft.server.world.ServerWorld; @@ -26,6 +27,12 @@ public class LeavesDrill extends DrillBase { return blockState.isIn(leavesTag); } + @Override + public boolean isRightTool(BlockPos pos) { + var blockState = session.world.getBlockState(pos); + return session.player.getMainHandStack().isSuitableFor(blockState) || session.player.getMainHandStack().getItem() == Items.SHEARS; + } + @Override public boolean drill(BlockPos startPos) { ServerWorld world = session.world; From 35148c2159d5b3dc6c13b8869eb3184b368a498e Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 10:20:47 +0200 Subject: [PATCH 17/27] Added Replenish function (replanting crops) --- .../wtf/hak/survivalfabric/config/Config.java | 4 +- .../hak/survivalfabric/mixin/BlockMixin.java | 62 +++++++++++++++++++ src/main/resources/survivalfabric.mixins.json | 1 + 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java diff --git a/src/main/java/wtf/hak/survivalfabric/config/Config.java b/src/main/java/wtf/hak/survivalfabric/config/Config.java index c53da01..c22301e 100644 --- a/src/main/java/wtf/hak/survivalfabric/config/Config.java +++ b/src/main/java/wtf/hak/survivalfabric/config/Config.java @@ -8,7 +8,7 @@ import java.util.List; public class Config { - public String configVersion = "1.0"; + public String configVersion = "1.1"; public boolean joinMessageEnabled = true; public String joinMessage = "§8[§a+§8] §7%s"; @@ -40,6 +40,8 @@ public class Config { public boolean chatCalcEnabled = true; + public boolean replenishEnabled = false; + public ScreenHandlerType screenHandlerType() { return switch (sharedEnderChestRows) { case 1 -> ScreenHandlerType.GENERIC_9X1; diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java new file mode 100644 index 0000000..2dd9742 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java @@ -0,0 +1,62 @@ +package wtf.hak.survivalfabric.mixin; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.CropBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.HoeItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.stat.Stats; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.GameMode; +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.CallbackInfoReturnable; +import wtf.hak.survivalfabric.config.ConfigManager; + +import java.util.List; + +@Mixin(Block.class) +public abstract class BlockMixin { + + @Inject(method = "onBreak", at = @At("HEAD"), cancellable = true) + public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfoReturnable cir) { + if (world.isClient()) return; + + if (state.getBlock() instanceof CropBlock && ConfigManager.getConfig().replenishEnabled) { + ItemStack mainHand = player.getStackInHand(Hand.MAIN_HAND); + if (mainHand.getItem() instanceof HoeItem) { + Item seedItem = state.getBlock().asItem(); + Block seedBlock = state.getBlock(); + List drops = Block.getDroppedStacks(state, (ServerWorld) world, pos, null, player, mainHand); + if (removeIfAvailable(drops, seedItem)) { + if(player.getGameMode() != GameMode.CREATIVE) { + for (ItemStack drop : drops) { + Block.dropStack(world, pos, drop); + } + } + world.getServer().executeSync(() -> { + world.setBlockState(pos, seedBlock.getDefaultState()); + }); + player.incrementStat(Stats.USED.getOrCreateStat(seedItem)); + cir.cancel(); + } + } + } + } + + private boolean removeIfAvailable(List drops, Item item) { + for (ItemStack drop : drops) { + if (drop.getItem() == item) { + drop.decrement(1); + return true; + } + } + return false; + } +} diff --git a/src/main/resources/survivalfabric.mixins.json b/src/main/resources/survivalfabric.mixins.json index 4795188..cadd775 100644 --- a/src/main/resources/survivalfabric.mixins.json +++ b/src/main/resources/survivalfabric.mixins.json @@ -3,6 +3,7 @@ "package": "wtf.hak.survivalfabric.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ + "BlockMixin", "PlayerManagerMixin", "ServerPlayerEntityMixin", "ServerPlayNetworkHandlerMixin", From 61d2a0b1376ddd55ae22dfc76bbdc72543d58363 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 10:20:58 +0200 Subject: [PATCH 18/27] Updated README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 8bfde8d..1dbc2f3 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,15 @@ As a challenge I'm trying to make it as user-friendly as possible. # Currently working on +## Server Side +- Vein Miner + - [ ] Configurable animation +- [x] Replenish + + +- Patches + - [x] Made leaves veinmineable using shears + ## Client Side - [x] Render block entities from a longer range From deb338f2c8f9e66383b7c2de1e51b5a2e85fa168 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 11:59:32 +0200 Subject: [PATCH 19/27] Added configurable Vein Miner animation --- README.md | 13 ++-- .../hak/survivalfabric/SurvivalFabric.java | 2 + .../wtf/hak/survivalfabric/config/Config.java | 1 + .../features/veinminer/drills/DrillBase.java | 51 ++++++++++++-- .../veinminer/drills/LeavesDrill.java | 49 +------------ .../features/veinminer/drills/OreDrill.java | 50 +------------- .../features/veinminer/drills/WoodDrill.java | 68 +------------------ .../hak/survivalfabric/utils/Scheduler.java | 45 ++++++++++++ 8 files changed, 103 insertions(+), 176 deletions(-) create mode 100644 src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java diff --git a/README.md b/README.md index 1dbc2f3..284b331 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ As a challenge I'm trying to make it as user-friendly as possible. - Open/close EC block while opening/closing SEC - Play open & close sounds - Vein miner +Code inspired by Inferis! ![VeinMiner](https://i.imgur.com/zOXWMNa.gif) - Chat Calculator @@ -52,14 +53,14 @@ As a challenge I'm trying to make it as user-friendly as possible. # Currently working on ## Server Side +- [x] Chat Calculator + - [x] Check if operator is present before calculating - Vein Miner - - [ ] Configurable animation + - [x] Configurable animation (tick delay) + - [x] Leaves veinmineable using shears - [x] Replenish -- Patches - - [x] Made leaves veinmineable using shears - ## Client Side - [x] Render block entities from a longer range @@ -72,8 +73,6 @@ As a challenge I'm trying to make it as user-friendly as possible. - Boolean - Float - Integer -- [x] Chat Calculator - - [x] Check if operator is present before calculating - [x] Zoom - [ ] Configurable @@ -81,7 +80,7 @@ As a challenge I'm trying to make it as user-friendly as possible. ## 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 diff --git a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java index 6bb8876..3d98cad 100644 --- a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java +++ b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java @@ -12,6 +12,7 @@ import wtf.hak.survivalfabric.commands.SlimeChunkCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.features.sharedenderchest.SharedEnderChest; import wtf.hak.survivalfabric.features.veinminer.VeinMinerEvents; +import wtf.hak.survivalfabric.utils.Scheduler; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; @@ -23,6 +24,7 @@ public class SurvivalFabric implements ModInitializer { @Override public void onInitialize() { + Scheduler.initialize(); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, "spectator", "s", "S", "camera", "c", "C")); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, "reloadsurvivalconfig")); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SlimeChunkCommand.register(dispatcher, "slimechunk", "sc")); diff --git a/src/main/java/wtf/hak/survivalfabric/config/Config.java b/src/main/java/wtf/hak/survivalfabric/config/Config.java index c22301e..672cfa8 100644 --- a/src/main/java/wtf/hak/survivalfabric/config/Config.java +++ b/src/main/java/wtf/hak/survivalfabric/config/Config.java @@ -37,6 +37,7 @@ public class Config { public boolean veinMinerEnabled = true; public int maxVeinSize = 99999; + public long veinAnimationTicks = 0; public boolean chatCalcEnabled = true; diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java index edf69da..d6b6630 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java @@ -1,6 +1,8 @@ package wtf.hak.survivalfabric.features.veinminer.drills; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.registry.tag.TagKey; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.MutableText; import net.minecraft.text.PlainTextContent; @@ -8,27 +10,66 @@ import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.Drill; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; +import wtf.hak.survivalfabric.utils.Scheduler; import java.util.ArrayList; +import java.util.List; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; +import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; public class DrillBase implements Drill { protected VeinMinerSession session; - public DrillBase(VeinMinerSession session) { + protected TagKey tag; + + public DrillBase(VeinMinerSession session, TagKey tag) { this.session = session; + this.tag = tag; } @Override - public boolean canHandle(BlockState blockId) { - return false; + public boolean canHandle(BlockState blockState) { + return blockState.isIn(tag); } @Override - public boolean drill(BlockPos blockPos) { - return false; + public boolean drill(BlockPos startPos) { + handleBlock(session.world.getBlockState(startPos).getBlock(), new ArrayList<>(), startPos, 0); + return true; + } + + private void handleBlock(Block initialBlock, List history, BlockPos pos, int brokenBlocks) { + if(brokenBlocks < getConfig().maxVeinSize) { + history.add(pos); + if(tryBreakBlock(pos)) { + brokenBlocks++; + int finalBrokenBlocks = brokenBlocks; + + // Put everything in a list to avoid scheduling a lot of tasks. + // Has the added benefit of only playing one sound + + List toBreak = new ArrayList<>(); + forXYZ(pos, 1, newPos -> { + if (!history.contains(newPos) && session.world.getBlockState(newPos).getBlock() == initialBlock) { + toBreak.add(newPos); + } + }); + + long delay = getConfig().veinAnimationTicks; + if(delay <= 0) + for(BlockPos newPos : toBreak) + handleBlock(initialBlock, history, newPos, finalBrokenBlocks); + else { + Scheduler.get().scheduleTask(() -> { + for(BlockPos newPos : toBreak) + handleBlock(initialBlock, history, newPos, finalBrokenBlocks); + }, delay); + } + + } + } } @Override diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java index eb614c0..7de9a6c 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java @@ -1,30 +1,16 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.item.Items; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class LeavesDrill extends DrillBase { - public static final TagKey leavesTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves")); - public LeavesDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(leavesTag); + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves"))); } @Override @@ -33,37 +19,4 @@ public class LeavesDrill extends DrillBase { return session.player.getMainHandStack().isSuitableFor(blockState) || session.player.getMainHandStack().getItem() == Items.SHEARS; } - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - Block initialBlock = world.getBlockState(startPos).getBlock(); - int brokenLeaves = 0; - ArrayDeque pending = new ArrayDeque(); - pending.add(startPos); - - while (!pending.isEmpty() && brokenLeaves < getConfig().maxVeinSize) { - BlockPos leavesPos = pending.remove(); - Block leavesBlock = world.getBlockState(leavesPos).getBlock(); - if (tryBreakBlock(leavesPos)) { - if (leavesBlock == initialBlock) { - brokenLeaves += 1; - } - - if (leavesBlock == initialBlock) { - // look around current block - forXYZ(leavesPos, 1, newPos -> { - BlockState newBlockState = world.getBlockState(newPos); - Block newBlock = newBlockState.getBlock(); - boolean isSameOreBlock = newBlock == leavesBlock; - if (!pending.contains(newPos) && isSameOreBlock) { - pending.add(newPos); - } - }); - } - } - } - - return true; - } - } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java index 55675cc..e607bf4 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java @@ -1,62 +1,14 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class OreDrill extends DrillBase { - public static final TagKey oreTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore")); - public OreDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(oreTag); - } - - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - Block initialBlock = world.getBlockState(startPos).getBlock(); - int brokenOre = 0; - ArrayDeque pending = new ArrayDeque(); - pending.add(startPos); - - while (!pending.isEmpty() && brokenOre < getConfig().maxVeinSize) { - BlockPos orePos = pending.remove(); - Block oreBlock = world.getBlockState(orePos).getBlock(); - if (tryBreakBlock(orePos)) { - if (oreBlock == initialBlock) { - brokenOre += 1; - } - - if (oreBlock == initialBlock) { - // look around current block - forXYZ(orePos, 1, newPos -> { - BlockState newBlockState = world.getBlockState(newPos); - Block newBlock = newBlockState.getBlock(); - boolean isSameOreBlock = newBlock == oreBlock; - if (!pending.contains(newPos) && isSameOreBlock) { - pending.add(newPos); - } - }); - } - } - } - - return true; + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore"))); } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java index 1c32d9b..d7d5bb1 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java @@ -1,80 +1,14 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.registry.Registries; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class WoodDrill extends DrillBase { - public static final TagKey woodTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood")); - public WoodDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(woodTag); - } - - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - int broken = 0; - ArrayDeque pendingLogs = new ArrayDeque<>(); - ArrayDeque logBlocks = new ArrayDeque<>(); - pendingLogs.add(startPos); - - String leavesBlockId = Registries.BLOCK.getId(world.getBlockState(startPos).getBlock()).toString().replace("_log", "_leaves"); - - while (!pendingLogs.isEmpty() && broken < getConfig().maxVeinSize) { - BlockPos woodPos = pendingLogs.remove(); - Block woodBlock = world.getBlockState(woodPos).getBlock(); - - if (tryBreakBlock(woodPos)) { - logBlocks.add(woodPos); - broken += 1; - - // look around current block - forXYZ(woodPos, 1, newPos -> { - Block newBlock = world.getBlockState(newPos).getBlock(); - if (newBlock == woodBlock && !pendingLogs.contains(newPos)) { - pendingLogs.add(newPos); - } - }, true); - } - } - - ArrayDeque pendingLeaves = logBlocks; - while (!pendingLeaves.isEmpty() && broken < getConfig().maxVeinSize) { - broken += forXYZ(pendingLeaves.remove(), 1, newPos -> { - int brokenLeaves = 0; - Block newBlock = world.getBlockState(newPos).getBlock(); - String newBlockId = Registries.BLOCK.getId(newBlock).toString(); - if (newBlockId.equals(leavesBlockId)) { - if (tryBreakBlock(newPos)) { - brokenLeaves += 1; - } - } - return brokenLeaves; - }, true); - } - - return true; - } - - private boolean isLeaf(Block block) { - return Registries.BLOCK.getId(block).toString().endsWith("_leaves"); + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood"))); } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java b/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java new file mode 100644 index 0000000..37c5e99 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java @@ -0,0 +1,45 @@ +package wtf.hak.survivalfabric.utils; + +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Scheduler { + + private static Scheduler INSTANCE; + + private final Map tasks = new ConcurrentHashMap<>(); + + public Scheduler() { + ServerTickEvents.END_SERVER_TICK.register((server) -> { + for(Runnable task : tasks.keySet()) { + long delay = tasks.get(task); + if(delay <= 0) { + task.run(); + tasks.remove(task); + } else { + tasks.put(task, delay-1); + } + } + }); + } + + public void scheduleTask(Runnable task) { + scheduleTask(task, 0L); + } + + public void scheduleTask(Runnable task, long delay) { + tasks.put(task, delay); + } + + public static Scheduler get() { + return INSTANCE; + } + + public static Scheduler initialize() { + INSTANCE = new Scheduler(); + return INSTANCE; + } +} From 151fd022c43c3de70625ee96d919f4f266a851e5 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 12:06:39 +0200 Subject: [PATCH 20/27] Cleaned up code --- .../features/veinminer/drills/DrillBase.java | 10 +++++----- .../java/wtf/hak/survivalfabric/mixin/BlockMixin.java | 6 ++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java index d6b6630..7885691 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java @@ -41,9 +41,9 @@ public class DrillBase implements Drill { } private void handleBlock(Block initialBlock, List history, BlockPos pos, int brokenBlocks) { - if(brokenBlocks < getConfig().maxVeinSize) { + if (brokenBlocks < getConfig().maxVeinSize) { history.add(pos); - if(tryBreakBlock(pos)) { + if (tryBreakBlock(pos)) { brokenBlocks++; int finalBrokenBlocks = brokenBlocks; @@ -58,12 +58,12 @@ public class DrillBase implements Drill { }); long delay = getConfig().veinAnimationTicks; - if(delay <= 0) - for(BlockPos newPos : toBreak) + if (delay <= 0) + for (BlockPos newPos : toBreak) handleBlock(initialBlock, history, newPos, finalBrokenBlocks); else { Scheduler.get().scheduleTask(() -> { - for(BlockPos newPos : toBreak) + for (BlockPos newPos : toBreak) handleBlock(initialBlock, history, newPos, finalBrokenBlocks); }, delay); } diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java index 2dd9742..59049ab 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java @@ -35,14 +35,12 @@ public abstract class BlockMixin { Block seedBlock = state.getBlock(); List drops = Block.getDroppedStacks(state, (ServerWorld) world, pos, null, player, mainHand); if (removeIfAvailable(drops, seedItem)) { - if(player.getGameMode() != GameMode.CREATIVE) { + if (player.getGameMode() != GameMode.CREATIVE) { for (ItemStack drop : drops) { Block.dropStack(world, pos, drop); } } - world.getServer().executeSync(() -> { - world.setBlockState(pos, seedBlock.getDefaultState()); - }); + world.getServer().executeSync(() -> world.setBlockState(pos, seedBlock.getDefaultState())); player.incrementStat(Stats.USED.getOrCreateStat(seedItem)); cir.cancel(); } From 1ae193ada77b9aa15234b23f393140b36edc3238 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 20:31:13 +0200 Subject: [PATCH 21/27] Greatly improved Vein Miner performance --- .../wtf/hak/survivalfabric/features/Zoom.java | 27 +++- .../features/veinminer/drills/DrillBase.java | 126 ++++++++++-------- 2 files changed, 94 insertions(+), 59 deletions(-) diff --git a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java index dfc8ec0..d5d45f1 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/Zoom.java +++ b/src/client/java/wtf/hak/survivalfabric/features/Zoom.java @@ -7,24 +7,37 @@ import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.Unique; + +import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig; public class Zoom { private static final KeyBinding ZOOM_BIND = KeyBindingHelper.registerKeyBinding(new KeyBinding("key.survivalfabric.zoom", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_C, "category.survivalfabric.survivalfabric")); - private static final float INITIAL_ZOOM = 20f; private static boolean SHOULD_ZOOM = false; - private static float ZOOM_STEP_VALUE = 2.5f; private static int ZOOM_STEP = 0; + @Unique + private static boolean initialSmoothZoom; + public static void register() { ClientTickEvents.END_CLIENT_TICK.register(client -> { - if (ZOOM_BIND.isPressed()) + + if (ZOOM_BIND.isPressed() && !SHOULD_ZOOM) { + if(getConfig().smoothCamera) { + initialSmoothZoom = MinecraftClient.getInstance().options.smoothCameraEnabled; + MinecraftClient.getInstance().options.smoothCameraEnabled = true; + } SHOULD_ZOOM = true; - else { + } else if (!ZOOM_BIND.isPressed() && SHOULD_ZOOM) { SHOULD_ZOOM = false; ZOOM_STEP = 0; + if(getConfig().smoothCamera) { + MinecraftClient.getInstance().options.smoothCameraEnabled = initialSmoothZoom; + } } + }); } @@ -33,7 +46,7 @@ public class Zoom { } public static float getZoomFov() { - return INITIAL_ZOOM - -ZOOM_STEP * ZOOM_STEP_VALUE; + return getConfig().initialZoom - -ZOOM_STEP * getConfig().zoomStep; } public static void modifyStep(int step) { @@ -42,9 +55,9 @@ public class Zoom { // Clamp the zoom level so the FOV stays within [1, 110] float zoomFov = getZoomFov(); if (zoomFov < 1) { - ZOOM_STEP = Math.round((1 - INITIAL_ZOOM) / ZOOM_STEP_VALUE) + 1; + ZOOM_STEP = Math.round((1 - getConfig().initialZoom) / getConfig().zoomStep) + 1; } else if (zoomFov > 110) { - ZOOM_STEP = Math.round((110 - INITIAL_ZOOM) / ZOOM_STEP_VALUE); + ZOOM_STEP = Math.round((110 - getConfig().initialZoom) / getConfig().zoomStep); } } } diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java index 7885691..72c6350 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java @@ -13,7 +13,9 @@ import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; import wtf.hak.survivalfabric.utils.Scheduler; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; @@ -21,7 +23,6 @@ import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; public class DrillBase implements Drill { protected VeinMinerSession session; - protected TagKey tag; public DrillBase(VeinMinerSession session, TagKey tag) { @@ -36,11 +37,11 @@ public class DrillBase implements Drill { @Override public boolean drill(BlockPos startPos) { - handleBlock(session.world.getBlockState(startPos).getBlock(), new ArrayList<>(), startPos, 0); + handleBlock(session.world.getBlockState(startPos).getBlock(), new HashSet<>(), startPos, 0); return true; } - private void handleBlock(Block initialBlock, List history, BlockPos pos, int brokenBlocks) { + private void handleBlock(Block initialBlock, Set history, BlockPos pos, int brokenBlocks) { if (brokenBlocks < getConfig().maxVeinSize) { history.add(pos); if (tryBreakBlock(pos)) { @@ -48,30 +49,40 @@ public class DrillBase implements Drill { int finalBrokenBlocks = brokenBlocks; // Put everything in a list to avoid scheduling a lot of tasks. - // Has the added benefit of only playing one sound - - List toBreak = new ArrayList<>(); - forXYZ(pos, 1, newPos -> { - if (!history.contains(newPos) && session.world.getBlockState(newPos).getBlock() == initialBlock) { - toBreak.add(newPos); - } - }); + Set toBreak = new HashSet<>(); + collectAdjacentBlocks(pos, history, initialBlock, toBreak); long delay = getConfig().veinAnimationTicks; - if (delay <= 0) - for (BlockPos newPos : toBreak) - handleBlock(initialBlock, history, newPos, finalBrokenBlocks); - else { - Scheduler.get().scheduleTask(() -> { - for (BlockPos newPos : toBreak) - handleBlock(initialBlock, history, newPos, finalBrokenBlocks); - }, delay); - } + // Use final or effectively final variables for lambda + final int[] finalBrokenBlocksArr = {finalBrokenBlocks}; + + if (delay <= 0) { + handleBlocksInBatch(toBreak, initialBlock, history, finalBrokenBlocksArr[0]); + } else { + Scheduler.get().scheduleTask(() -> handleBlocksInBatch(toBreak, initialBlock, history, finalBrokenBlocksArr[0]), delay); + } } } } + private void collectAdjacentBlocks(BlockPos pos, Set history, Block initialBlock, Set toBreak) { + forXYZ(pos, 1, new ForXYZHandler() { + @Override + public void handle(BlockPos newPos) { + if (!history.contains(newPos) && session.world.getBlockState(newPos).getBlock() == initialBlock) { + toBreak.add(newPos); + } + } + }); + } + + private void handleBlocksInBatch(Set toBreak, Block initialBlock, Set history, int finalBrokenBlocks) { + for (BlockPos newPos : toBreak) { + handleBlock(initialBlock, history, newPos, finalBrokenBlocks); + } + } + @Override public boolean isRightTool(BlockPos pos) { var blockState = session.world.getBlockState(pos); @@ -79,9 +90,12 @@ public class DrillBase implements Drill { } protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler) { - forXYZ(pos, max, handlerPos -> { - handler.handle(handlerPos); - return 0; + forXYZ(pos, max, new ForXYZCounter() { + @Override + public int handle(BlockPos pos) { + handler.handle(pos); + return 1; // Accumulate one count for each position processed + } }, false); } @@ -90,14 +104,17 @@ public class DrillBase implements Drill { } protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler, boolean forceVertical) { - forXYZ(pos, max, handlerPos -> { - handler.handle(handlerPos); - return 0; + forXYZ(pos, max, new ForXYZCounter() { + @Override + public int handle(BlockPos pos) { + handler.handle(pos); + return 1; // Accumulate one count for each position processed + } }, forceVertical); } protected int forXYZ(BlockPos pos, int max, ForXYZCounter handler, boolean forceVertical) { - ArrayList offsets = new ArrayList(); + Set offsets = new HashSet<>(); for (int d = 0; d <= max; ++d) { offsets.add(d); if (d != -d) { @@ -105,39 +122,21 @@ public class DrillBase implements Drill { } } - String[] order = new String[]{"x", "y", "z"}; - if (forceVertical) { - order = new String[]{"y", "x", "z"}; - } else { - ServerPlayerEntity player = session.player; - boolean majorPitchChange = player.getPitch() < -45.0 || player.getPitch() > 45.0; - boolean majorYawChange = (player.getYaw() > 45.0 && player.getYaw() < 135.0) || (player.getYaw() < -45.0 && player.getYaw() > -135.0); - if (majorPitchChange) { - if (majorYawChange) { - order = new String[]{"y", "z", "x"}; - } else { - order = new String[]{"y", "x", "z"}; - } - } else { - if (majorYawChange) { - order = new String[]{"z", "y", "x"}; - } - } - } - + String[] order = determineOrder(forceVertical); int counter = 0; + for (int i1 : offsets) { for (int i2 : offsets) { for (int i3 : offsets) { - int ix = order[0] == "x" ? i1 : order[1] == "x" ? i2 : i3; - int iy = order[0] == "y" ? i1 : order[1] == "y" ? i2 : i3; - int iz = order[0] == "z" ? i1 : order[1] == "z" ? i2 : i3; + int ix = order[0].equals("x") ? i1 : order[1].equals("x") ? i2 : i3; + int iy = order[0].equals("y") ? i1 : order[1].equals("y") ? i2 : i3; + int iz = order[0].equals("z") ? i1 : order[1].equals("z") ? i2 : i3; int px = pos.getX() + ix; int py = pos.getY() + iy; int pz = pos.getZ() + iz; - counter += handler.handle(new BlockPos(px, py, pz)); + counter += handler.handle(new BlockPos(px, py, pz)); // Works with ForXYZCounter, returning an int } } } @@ -145,6 +144,29 @@ public class DrillBase implements Drill { return counter; } + protected String[] determineOrder(boolean forceVertical) { + if (forceVertical) { + return new String[]{"y", "x", "z"}; + } else { + ServerPlayerEntity player = session.player; + boolean majorPitchChange = player.getPitch() < -45.0 || player.getPitch() > 45.0; + boolean majorYawChange = (player.getYaw() > 45.0 && player.getYaw() < 135.0) || (player.getYaw() < -45.0 && player.getYaw() > -135.0); + + if (majorPitchChange) { + if (majorYawChange) { + return new String[]{"y", "z", "x"}; + } else { + return new String[]{"y", "x", "z"}; + } + } else { + if (majorYawChange) { + return new String[]{"z", "y", "x"}; + } + } + } + return new String[]{"x", "y", "z"}; + } + protected boolean tryBreakBlock(BlockPos blockPos) { session.addPosition(blockPos); boolean success = isRightTool(blockPos) && session.player.interactionManager.tryBreakBlock(blockPos); @@ -168,4 +190,4 @@ public class DrillBase implements Drill { protected interface ForXYZCounter { int handle(BlockPos pos); } -} \ No newline at end of file +} From 29067365ca0637c9504afa416f0e3037abe7b670 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 20:32:00 +0200 Subject: [PATCH 22/27] Added Zoom functionality --- .../wtf/hak/survivalfabric/config/client/ClientConfig.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java index 2611e7e..faf6ee0 100644 --- a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java +++ b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java @@ -1,7 +1,7 @@ package wtf.hak.survivalfabric.config.client; public class ClientConfig { - public String configVersion = "1.0"; + public String configVersion = "1.1"; public boolean renderNetherFog = false; public boolean renderOverworldFog = false; @@ -13,4 +13,7 @@ public class ClientConfig { public boolean lockTeleportHeadMovement = true; public boolean manipulateBlockEntityDistance = true; public int blockEntityRange = 512; + public boolean smoothCamera = true; + public float initialZoom = 20f; + public float zoomStep = 2.5f; } From 327c92a81c293a707cccf4caac2a26ee1a6599ba Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 20:32:24 +0200 Subject: [PATCH 23/27] Updated README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 284b331..c885d9c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Code inspired by Inferis! - Float - Integer - [x] Zoom - - [ ] Configurable + - [x] Configurable # To-do From 05a93b5ab0cc5ccbcb53c2f7b78538e7ca78e2ab Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 20:36:47 +0200 Subject: [PATCH 24/27] Updated README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c885d9c..12778da 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ As a challenge I'm trying to make it as user-friendly as possible. ## Server Side -### Features - Custom join message ![Join Message](https://i.imgur.com/7uv5lUb.png) - Custom quit message @@ -67,7 +66,8 @@ Code inspired by Inferis! - [x] Toggleable via GUI - [x] Configurable value - [x] In GUI -- [x] Rework Mod Menu integration to be more flexible +- [x] Rework Mod Menu integration to be more flexible +DISCLAIMER: this is NOT perfect and still needs to be reworked, I'm just too lazy right now... - The following types are accepted: - String - Boolean From ccbb15f7c4b0648faed11f7fcd7c193ea46f87d3 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 22:23:08 +0200 Subject: [PATCH 25/27] Fixed bug in Replenish feature --- src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java index 59049ab..a4422a0 100644 --- a/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java +++ b/src/main/java/wtf/hak/survivalfabric/mixin/BlockMixin.java @@ -2,7 +2,9 @@ package wtf.hak.survivalfabric.mixin; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; import net.minecraft.block.CropBlock; +import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.HoeItem; import net.minecraft.item.Item; @@ -39,9 +41,11 @@ public abstract class BlockMixin { for (ItemStack drop : drops) { Block.dropStack(world, pos, drop); } + player.incrementStat(Stats.USED.getOrCreateStat(seedItem)); + mainHand.damage(1, player, EquipmentSlot.MAINHAND); } world.getServer().executeSync(() -> world.setBlockState(pos, seedBlock.getDefaultState())); - player.incrementStat(Stats.USED.getOrCreateStat(seedItem)); + cir.setReturnValue(Blocks.AIR.getDefaultState()); cir.cancel(); } } From 2be7c16e8b3934c79db5394fef072e662d01b367 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Tue, 20 May 2025 11:59:51 +0200 Subject: [PATCH 26/27] Refactored SFKeyBindings to CameraShortcut --- .../java/wtf/hak/survivalfabric/SurvivalFabricClient.java | 4 ++-- .../features/{SFKeyBindings.java => CameraShortcut.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/client/java/wtf/hak/survivalfabric/features/{SFKeyBindings.java => CameraShortcut.java} (96%) diff --git a/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java b/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java index 26fc1e5..6af5e0a 100644 --- a/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java +++ b/src/client/java/wtf/hak/survivalfabric/SurvivalFabricClient.java @@ -4,7 +4,7 @@ import net.fabricmc.api.ClientModInitializer; import wtf.hak.survivalfabric.config.client.ClientConfigManager; import wtf.hak.survivalfabric.features.AngleViewer; import wtf.hak.survivalfabric.features.RemoveDarknessEffect; -import wtf.hak.survivalfabric.features.SFKeyBindings; +import wtf.hak.survivalfabric.features.CameraShortcut; import wtf.hak.survivalfabric.features.Zoom; public class SurvivalFabricClient implements ClientModInitializer { @@ -18,7 +18,7 @@ public class SurvivalFabricClient implements ClientModInitializer { // Features AngleViewer.register(); RemoveDarknessEffect.register(); - SFKeyBindings.register(); + CameraShortcut.register(); Zoom.register(); } } \ No newline at end of file diff --git a/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java b/src/client/java/wtf/hak/survivalfabric/features/CameraShortcut.java similarity index 96% rename from src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java rename to src/client/java/wtf/hak/survivalfabric/features/CameraShortcut.java index 6f97569..e6c072a 100644 --- a/src/client/java/wtf/hak/survivalfabric/features/SFKeyBindings.java +++ b/src/client/java/wtf/hak/survivalfabric/features/CameraShortcut.java @@ -6,7 +6,7 @@ import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import org.lwjgl.glfw.GLFW; -public class SFKeyBindings { +public class CameraShortcut { private static final KeyBinding CAMERA_BIND = KeyBindingHelper.registerKeyBinding(new KeyBinding("key.survivalfabric.camera", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_PERIOD, "category.survivalfabric.survivalfabric")); From a6f8645c685c95d87459c0bb78cf08255572c150 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Tue, 20 May 2025 12:01:59 +0200 Subject: [PATCH 27/27] Added toggleable scrollToZoom --- README.md | 4 ++++ .../wtf/hak/survivalfabric/config/client/ClientConfig.java | 1 + .../java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 12778da..ba7de1a 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,10 @@ DISCLAIMER: this is NOT perfect and still needs to be reworked, I'm just too laz - Integer - [x] Zoom - [x] Configurable + - Smooth zoom + - Initial zoom value + - Zoom step value + - Scroll to zoom further # To-do diff --git a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java index faf6ee0..c4ae18f 100644 --- a/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java +++ b/src/client/java/wtf/hak/survivalfabric/config/client/ClientConfig.java @@ -15,5 +15,6 @@ public class ClientConfig { public int blockEntityRange = 512; public boolean smoothCamera = true; public float initialZoom = 20f; + public boolean scrollToZoom = true; public float zoomStep = 2.5f; } diff --git a/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java index 283de8e..2f6074a 100644 --- a/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java +++ b/src/client/java/wtf/hak/survivalfabric/mixin/client/MouseMixin.java @@ -8,6 +8,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import wtf.hak.survivalfabric.features.Zoom; +import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig; + @Mixin(Mouse.class) public class MouseMixin { @@ -16,7 +18,7 @@ public class MouseMixin { */ @Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true) private void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) { - if (Zoom.isZooming()) { + if (Zoom.isZooming() && getConfig().scrollToZoom) { if (MinecraftClient.getInstance().player != null) { if (vertical > 0) Zoom.modifyStep(-1);