diff --git a/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java
new file mode 100644
index 00000000..7c486c92
--- /dev/null
+++ b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java
@@ -0,0 +1,31 @@
+/*
+ * Sol Client - an open source Minecraft client
+ * Copyright (C) 2021-2023 TheKodeToad and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.solclient.client.mod.impl.visibleseasons;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Snowflake {
+ private int x;
+ private int y;
+ private int size;
+ private int speed;
+}
diff --git a/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java
new file mode 100644
index 00000000..7eb8f48f
--- /dev/null
+++ b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java
@@ -0,0 +1,61 @@
+/*
+ * Sol Client - an open source Minecraft client
+ * Copyright (C) 2021-2023 TheKodeToad and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.solclient.client.mod.impl.visibleseasons;
+
+import com.google.gson.annotations.Expose;
+import io.github.solclient.client.mod.impl.StandardMod;
+import io.github.solclient.client.mod.option.annotation.Option;
+import io.github.solclient.client.mod.option.annotation.Slider;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.resource.language.I18n;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VisibleSeasonsMod extends StandardMod {
+ public static VisibleSeasonsMod instance;
+ protected final MinecraftClient mc = MinecraftClient.getInstance();
+ public List snowflakes = new ArrayList<>();
+
+ @Expose
+ @Option
+ public boolean forceVisibleSeasons;
+
+ @Expose
+ @Option
+ @Slider(min = 1, max = 50, step = 1)
+ public float visibleSeasonsAmount = 25;
+
+ @Expose
+ @Option
+ public boolean visibleSeasonsLowDetail;
+
+
+ @Override
+ public String getDetail() {
+ return I18n.translate("sol_client.mod.screen.by", "ArikSquad");
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ instance = this;
+ }
+
+}
diff --git a/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java b/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java
index f6ee9a29..2d8ae765 100644
--- a/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java
+++ b/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java
@@ -18,18 +18,30 @@
package io.github.solclient.client.ui.component;
-import org.apache.logging.log4j.*;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.input.*;
-
+import io.github.solclient.client.mod.impl.visibleseasons.Snowflake;
+import io.github.solclient.client.mod.impl.visibleseasons.VisibleSeasonsMod;
import io.github.solclient.client.ui.component.controller.ParentBoundsController;
-import io.github.solclient.client.util.*;
-import io.github.solclient.client.util.cursors.SystemCursors;
-import io.github.solclient.client.util.data.*;
+import io.github.solclient.client.util.MinecraftUtils;
+import io.github.solclient.client.util.NanoVGManager;
+import io.github.solclient.client.util.data.Colour;
+import io.github.solclient.client.util.data.Rectangle;
import lombok.Getter;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.Window;
+import net.minecraft.util.Identifier;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.nanovg.NVGPaint;
+import org.lwjgl.nanovg.NanoVG;
+
+import java.time.LocalDate;
+import java.time.Month;
+import java.util.Iterator;
+import java.util.concurrent.ThreadLocalRandom;
public class ComponentScreen extends Screen {
@@ -41,6 +53,9 @@ public class ComponentScreen extends Screen {
protected boolean background = true;
private float mouseX, mouseY;
+ private long lastFrameTime = 0;
+ private final long nvg;
+
public ComponentScreen(Component root) {
this.parentScreen = MinecraftClient.getInstance().currentScreen;
rootWrapper = new Component() {
@@ -53,6 +68,8 @@ public Rectangle getBounds() {
};
rootWrapper.setScreen(this);
+ nvg = NanoVGManager.getNvg();
+
rootWrapper.add(root, new ParentBoundsController());
this.root = root;
}
@@ -63,6 +80,46 @@ public Component getRoot() {
@Override
public void render(int mouseX, int mouseY, float tickDelta) {
+ if (VisibleSeasonsMod.instance.forceVisibleSeasons || LocalDate.now().getMonth() == Month.DECEMBER) {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastFrameTime >= ThreadLocalRandom.current().nextInt(400)) {
+ if (VisibleSeasonsMod.instance.snowflakes.size() < (int) VisibleSeasonsMod.instance.visibleSeasonsAmount) {
+ int snowflakeX = ThreadLocalRandom.current().nextInt(client.width);
+ int snowflakeSize = 5 + ThreadLocalRandom.current().nextInt(6);
+ int snowflakeSpeed = 1 + ThreadLocalRandom.current().nextInt(3);
+ VisibleSeasonsMod.instance.snowflakes.add(new Snowflake(snowflakeX, 0, snowflakeSize, snowflakeSpeed));
+ lastFrameTime = currentTime;
+ }
+ }
+
+
+ Iterator iterator = VisibleSeasonsMod.instance.snowflakes.iterator();
+ while (iterator.hasNext()) {
+ Snowflake snowflake = iterator.next();
+ int x = snowflake.getX();
+ int y = snowflake.getY();
+ int size = snowflake.getSize();
+
+ NanoVG.nvgBeginPath(nvg);
+ if (VisibleSeasonsMod.instance.visibleSeasonsLowDetail) {
+ NanoVG.nvgRect(nvg, x, y, size, size);
+ NanoVG.nvgFillColor(nvg, Colour.WHITE.nvg());
+ NanoVG.nvgFill(nvg);
+ } else {
+ NVGPaint paint = MinecraftUtils.nvgMinecraftTexturePaint(nvg, new Identifier("sol_client", "textures/gui/snowflake.png"), x, y, size, size, 0);
+ NanoVG.nvgFillPaint(nvg, paint);
+ NanoVG.nvgFill(nvg);
+ }
+
+ // Reset the snowflake if it goes beyond the screen bounds
+ if (y > client.height) {
+ iterator.remove();
+ } else {
+ snowflake.setY(y + snowflake.getSpeed());
+ }
+ }
+ }
+
try {
Window window = new Window(client);
diff --git a/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java b/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java
index d6f498bd..c712b446 100644
--- a/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java
+++ b/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java
@@ -18,23 +18,35 @@
package io.github.solclient.client.ui.screen.mods;
-import org.lwjgl.input.Keyboard;
-
import io.github.solclient.client.SolClient;
-import io.github.solclient.client.mod.*;
+import io.github.solclient.client.mod.Mod;
+import io.github.solclient.client.mod.ModUiStateManager;
import io.github.solclient.client.mod.impl.core.CoreMod;
-import io.github.solclient.client.ui.*;
-import io.github.solclient.client.ui.component.*;
-import io.github.solclient.client.ui.component.controller.*;
-import io.github.solclient.client.ui.component.impl.*;
+import io.github.solclient.client.ui.ScreenAnimation;
+import io.github.solclient.client.ui.Theme;
+import io.github.solclient.client.ui.component.Component;
+import io.github.solclient.client.ui.component.ComponentRenderInfo;
+import io.github.solclient.client.ui.component.controller.AlignedBoundsController;
+import io.github.solclient.client.ui.component.controller.Controller;
+import io.github.solclient.client.ui.component.impl.BlockComponent;
+import io.github.solclient.client.ui.component.impl.ButtonComponent;
+import io.github.solclient.client.ui.component.impl.LabelComponent;
+import io.github.solclient.client.ui.component.impl.TextFieldComponent;
import io.github.solclient.client.ui.screen.PanoramaBackgroundScreen;
-import io.github.solclient.client.util.*;
-import io.github.solclient.client.util.data.*;
+import io.github.solclient.client.util.ActiveMainMenu;
+import io.github.solclient.client.util.KeyBindingInterface;
+import io.github.solclient.client.util.MinecraftUtils;
+import io.github.solclient.client.util.data.Alignment;
+import io.github.solclient.client.util.data.Rectangle;
import lombok.Getter;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.language.I18n;
+import org.lwjgl.input.Keyboard;
+
public class ModsScreen extends PanoramaBackgroundScreen {
+ protected MinecraftClient mc = MinecraftClient.getInstance();
private final ModsScreenComponent component;
private final ScreenAnimation animation = new ScreenAnimation();
@@ -280,8 +292,25 @@ public boolean mouseClickedAnywhere(ComponentRenderInfo info, int button, boolea
return super.mouseClickedAnywhere(info, button, inside, processed);
}
+ public static final int[] keys = {
+ Keyboard.KEY_UP, Keyboard.KEY_UP, Keyboard.KEY_DOWN, Keyboard.KEY_DOWN, Keyboard.KEY_LEFT, Keyboard.KEY_RIGHT,
+ Keyboard.KEY_LEFT, Keyboard.KEY_RIGHT, Keyboard.KEY_B, Keyboard.KEY_A
+ };
+
+ private int keyIndex = 0;
+
@Override
public boolean keyPressed(ComponentRenderInfo info, int keyCode, char character) {
+ if (keyCode == keys[keyIndex]) {
+ keyIndex++;
+ if (keyIndex == keys.length) {
+ mc.setScreen(new SnakeScreen());
+ keyIndex = 0;
+ }
+ } else {
+ keyIndex = 0;
+ }
+
if ((screen.getRoot().getDialog() == null
&& (keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER))) {
if (mod == null) {
diff --git a/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java b/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java
new file mode 100644
index 00000000..25fd0eb2
--- /dev/null
+++ b/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java
@@ -0,0 +1,271 @@
+/*
+ * Sol Client - an open source Minecraft client
+ * Copyright (C) 2021-2023 TheKodeToad and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.solclient.client.ui.screen.mods;
+
+import io.github.solclient.client.SolClient;
+import io.github.solclient.client.mod.impl.visibleseasons.VisibleSeasonsMod;
+import io.github.solclient.client.mod.impl.core.CoreMod;
+import io.github.solclient.client.ui.ScreenAnimation;
+import io.github.solclient.client.ui.component.Component;
+import io.github.solclient.client.ui.component.ComponentRenderInfo;
+import io.github.solclient.client.ui.component.controller.AlignedBoundsController;
+import io.github.solclient.client.ui.component.impl.BlockComponent;
+import io.github.solclient.client.ui.component.impl.LabelComponent;
+import io.github.solclient.client.ui.screen.PanoramaBackgroundScreen;
+import io.github.solclient.client.util.ActiveMainMenu;
+import io.github.solclient.client.util.data.Alignment;
+import io.github.solclient.client.util.data.Colour;
+import io.github.solclient.client.util.data.Rectangle;
+import net.minecraft.client.MinecraftClient;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.nanovg.NanoVG;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class SnakeScreen extends PanoramaBackgroundScreen {
+
+ protected MinecraftClient mc = MinecraftClient.getInstance();
+ private final ScreenAnimation animation = new ScreenAnimation();
+
+ public SnakeScreen() {
+ super(new Component() {
+ {
+ add(new SnakeComponent(), new AlignedBoundsController(Alignment.CENTRE, Alignment.CENTRE));
+ }
+ });
+
+ background = false;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ Keyboard.enableRepeatEvents(true);
+ }
+
+ @Override
+ public void render(int mouseX, int mouseY, float tickDelta) {
+ if (client.world == null) {
+ if (CoreMod.instance.fancyMainMenu) {
+ background = false;
+ drawPanorama(mouseX, mouseY, tickDelta);
+ } else
+ background = true;
+ }
+
+ super.render(mouseX, mouseY, tickDelta);
+ }
+
+ @Override
+ protected void wrap(Runnable task) {
+ animation.wrap(task);
+ }
+
+ @Override
+ public void removed() {
+ super.removed();
+ animation.close();
+ SolClient.INSTANCE.saveAll();
+ Keyboard.enableRepeatEvents(false);
+ }
+
+ @Override
+ public void closeAll() {
+ if (client.world == null && CoreMod.instance.fancyMainMenu) {
+ client.setScreen(ActiveMainMenu.getInstance());
+ return;
+ }
+
+ super.closeAll();
+ }
+
+ public static class SnakeComponent extends BlockComponent {
+
+ public static final int[] keys = {Keyboard.KEY_S, Keyboard.KEY_E, Keyboard.KEY_A, Keyboard.KEY_S, Keyboard.KEY_O, Keyboard.KEY_N, Keyboard.KEY_S};
+ private int keyIndex = 0;
+
+ private int foodX = 0;
+ private int foodY = 0;
+
+ private int snakeLength = 1;
+ private final int snakeSize = 10;
+ private final List snakeX = new ArrayList<>();
+ private final List snakeY = new ArrayList<>();
+ private int snakeDirection = 3; // 0 = up, 1 = right, 2 = down, 3 = left
+
+ private long lastFrameTime = 0;
+ private final List nextMoves = new ArrayList<>();
+
+ private final int screenSize = 500;
+
+ public SnakeComponent() {
+ super(new Colour(0xFF202020));
+ add(new LabelComponent((component, defaultText) -> String.valueOf(snakeLength - 3)).scaled(1.45F), new AlignedBoundsController(Alignment.CENTRE, Alignment.START));
+
+ resetSnake();
+ generateFood();
+ }
+
+ @Override
+ public void render(ComponentRenderInfo info) {
+ super.render(info);
+
+ renderSnake();
+ renderFood();
+
+ if (isCollidingFood()) {
+ generateFood();
+ addFood();
+ }
+
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastFrameTime >= 50) {
+ moveSnake();
+ lastFrameTime = currentTime;
+ }
+ }
+
+ private void addFood() {
+ snakeLength++;
+ int lastIndex = snakeLength - 1;
+ snakeX.add(snakeX.get(lastIndex - 1));
+ snakeY.add(snakeY.get(lastIndex - 1));
+ }
+
+ private boolean isCollidingFood() {
+ return snakeX.get(0) == foodX && snakeY.get(0) == foodY;
+ }
+
+ private void generateFood() {
+ foodX = (int) (Math.random() * screenSize);
+ foodY = (int) (Math.random() * screenSize);
+ foodX = foodX - (foodX % snakeSize);
+ foodY = foodY - (foodY % snakeSize);
+ }
+
+ private void moveSnake() {
+ if (nextMoves.size() > 0) {
+ changeDirection(nextMoves.get(0));
+ nextMoves.remove(0);
+ }
+
+ for (int i = snakeLength - 1; i > 0; i--) {
+ snakeX.set(i, snakeX.get(i - 1));
+ snakeY.set(i, snakeY.get(i - 1));
+ }
+
+ if (snakeDirection == 0) {
+ snakeY.set(0, snakeY.get(0) - snakeSize);
+ } else if (snakeDirection == 1) {
+ snakeX.set(0, snakeX.get(0) + snakeSize);
+ } else if (snakeDirection == 2) {
+ snakeY.set(0, snakeY.get(0) + snakeSize);
+ } else if (snakeDirection == 3) {
+ snakeX.set(0, snakeX.get(0) - snakeSize);
+ }
+
+ // Check if the new head position is within bounds
+ if (snakeX.get(0) < 0 || snakeX.get(0) >= screenSize || snakeY.get(0) < 0 || snakeY.get(0) >= screenSize) {
+ resetSnake();
+ }
+ }
+
+
+ private void renderSnake() {
+ for (int i = 0; i < snakeLength; i++) {
+ if (i == 0) {
+ NanoVG.nvgBeginPath(nvg);
+ NanoVG.nvgRect(nvg, snakeX.get(i), snakeY.get(i), snakeSize, snakeSize);
+ NanoVG.nvgFillColor(nvg, new Colour(0xFF5dd95b).nvg());
+ NanoVG.nvgFill(nvg);
+ } else {
+ NanoVG.nvgBeginPath(nvg);
+ NanoVG.nvgRect(nvg, snakeX.get(i), snakeY.get(i), snakeSize, snakeSize);
+ NanoVG.nvgFillColor(nvg, new Colour(0xFF69ff67).nvg());
+ NanoVG.nvgFill(nvg);
+ }
+ }
+ }
+
+ private void renderFood() {
+ NanoVG.nvgBeginPath(nvg);
+ NanoVG.nvgRect(nvg, foodX, foodY, snakeSize, snakeSize);
+ NanoVG.nvgFillColor(nvg, new Colour(0xFFfe6666).nvg());
+ NanoVG.nvgFill(nvg);
+ }
+
+ private void resetSnake() {
+ snakeLength = 3;
+ snakeDirection = 3;
+ snakeX.clear();
+ snakeY.clear();
+
+ for (int i = 0; i < snakeLength; i++) {
+ snakeX.add((screenSize / 2) - (i * snakeSize));
+ snakeY.add(screenSize / 2);
+ }
+
+ moveSnake();
+ }
+
+ public void changeDirection(int newDirection) {
+ // Prevent the snake from immediately reversing its direction
+ if (Math.abs(snakeDirection - newDirection) != 2) {
+ snakeDirection = newDirection;
+ }
+ }
+
+ @Override
+ public boolean keyPressed(ComponentRenderInfo info, int keyCode, char character) {
+ if (keyCode == keys[keyIndex]) {
+ keyIndex++;
+ if (keyIndex == keys.length) {
+ mc.setScreen(new ModsScreen(VisibleSeasonsMod.instance));
+ keyIndex = 0;
+ }
+ } else {
+ keyIndex = 0;
+ }
+
+ if (keyCode == Keyboard.KEY_UP) {
+ nextMoves.add(0);
+ }
+ if (keyCode == Keyboard.KEY_RIGHT) {
+ nextMoves.add(1);
+ }
+ if (keyCode == Keyboard.KEY_DOWN) {
+ nextMoves.add(2);
+ }
+ if (keyCode == Keyboard.KEY_LEFT) {
+ nextMoves.add(3);
+ }
+
+ return super.keyPressed(info, keyCode, character);
+ }
+
+ @Override
+ public Rectangle getDefaultBounds() {
+ return Rectangle.ofDimensions(screenSize, screenSize);
+ }
+
+ }
+
+}
diff --git a/src/main/resources/assets/sol_client/lang/en_US.lang b/src/main/resources/assets/sol_client/lang/en_US.lang
index dd990724..a410612c 100644
--- a/src/main/resources/assets/sol_client/lang/en_US.lang
+++ b/src/main/resources/assets/sol_client/lang/en_US.lang
@@ -427,6 +427,12 @@ sol_client.mod.screenshots.deleted=The screenshot was deleted
sol_client.mod.chat_hotkeys.name=Chat Hotkeys
sol_client.mod.chat_hotkeys.description=Bind commands and messages to keys.
+sol_client.mod.visible_seasons.name=Visible Seasons
+sol_client.mod.visible_seasons.description=Render season decorations in the module menu.
+sol_client.mod.visible_seasons.option.forceVisibleSeasons=Force season decorations
+sol_client.mod.visible_seasons.option.visibleSeasonsAmount=Amount of decorations
+sol_client.mod.visible_seasons.option.visibleSeasonsLowDetail=Low detail mode
+
sol_client.mod.chat_hotkeys.option.hotkeys=Hotkeys
sol_client.mod.chat_hotkeys.option.cooldown=Rate limit
diff --git a/src/main/resources/assets/sol_client/lang/fi_FI.lang b/src/main/resources/assets/sol_client/lang/fi_FI.lang
index 3e162b5b..7068fb04 100644
--- a/src/main/resources/assets/sol_client/lang/fi_FI.lang
+++ b/src/main/resources/assets/sol_client/lang/fi_FI.lang
@@ -494,3 +494,9 @@ sol_client.mod.toggle_sprint.sneak_toggled=Hiipiminen (kytketty)
sol_client.mod.screenshots.delete=Poista
sol_client.mod.screenshots.delete_error=Poiston aikana tapahtui virhe: %s. Sinä voit yrittää avata %s sen sijaan.
sol_client.slider.pixels=%spx
+
+sol_client.mod.visible_seasons.name=Näkyvät vuodenajat
+sol_client.mod.visible_seasons.description=Näytä vuodenaikaan liittyvät koristeet moduulivalikossa.
+sol_client.mod.visible_seasons.option.forceVisibleSeasons=Pakota vuodenaika koristeet
+sol_client.mod.visible_seasons.option.visibleSeasonsAmount=Koristeiden määrä
+sol_client.mod.visible_seasons.option.visibleSeasonsLowDetail=Alhainen yksityiskohtaisuus
diff --git a/src/main/resources/assets/sol_client/textures/gui/snowflake.png b/src/main/resources/assets/sol_client/textures/gui/snowflake.png
new file mode 100644
index 00000000..64dfebe4
Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/gui/snowflake.png differ
diff --git a/src/main/resources/standard-mods.json b/src/main/resources/standard-mods.json
index e285cb70..fff0029c 100644
--- a/src/main/resources/standard-mods.json
+++ b/src/main/resources/standard-mods.json
@@ -255,6 +255,12 @@
"main": "HitColourMod",
"category": "visual"
},
+ {
+ "id": "visible_seasons",
+ "main": "visibleseasons.VisibleSeasonsMod",
+ "category": "hidden",
+ "forcedOn": true
+ },
// integration
{
"id": "hypixel_util",