aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWyvest <45589059+Wyvest@users.noreply.github.com>2022-05-28 12:02:04 +0700
committerGitHub <noreply@github.com>2022-05-28 12:02:04 +0700
commit01e4c0dfb2a9726a9bf1c4d1a1fe28edbc3d094c (patch)
tree2949a7d1a9e6a5e9fbf6db983491ab813614b889
parent2f45fed981d7fcdf6e39791fecfa1dd410614fbf (diff)
downloadOneConfig-01e4c0dfb2a9726a9bf1c4d1a1fe28edbc3d094c.tar.gz
OneConfig-01e4c0dfb2a9726a9bf1c4d1a1fe28edbc3d094c.tar.bz2
OneConfig-01e4c0dfb2a9726a9bf1c4d1a1fe28edbc3d094c.zip
OC-32 OC-34 (#24)
* OC-34 hypixel utils class (#21) * OC-26 Build Workflow * OC-26 oops * OC-33 some networking utils * OC-34 idk * OC-34 idk * OC-34 restructure hypixel utils * hypixelutils and multithreading implement more events remove vcal icon stuff * more javadocs Co-authored-by: Ethan <git@ethanlibs.co>
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/OneConfig.java4
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/events/EventManager.java12
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java12
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/events/event/LocrawEvent.java11
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/events/event/WorldLoadEvent.java4
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/gui/pages/CreditsPage.java3
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/lwjgl/BlurHandler.java8
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/mixin/GuiIngameForgeMixin.java6
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/mixin/MinecraftMixin.java20
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/mixin/NetHandlerPlayClientMixin.java24
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/mixin/WorldClientMixin.java21
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/plugin/asm/tweakers/VigilantTransformer.java3
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/IOUtils.java3
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/InputUtils.java55
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/JsonUtils.java22
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/Multithreading.java75
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/NetworkUtils.java106
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java13
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/TickDelay.java9
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/hypixel/HypixelUtils.java159
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/utils/hypixel/LocrawInfo.java110
-rw-r--r--src/main/resources/mixins.oneconfig.json4
22 files changed, 638 insertions, 46 deletions
diff --git a/src/main/java/cc/polyfrost/oneconfig/OneConfig.java b/src/main/java/cc/polyfrost/oneconfig/OneConfig.java
index 0eb9ec3..60bb37e 100644
--- a/src/main/java/cc/polyfrost/oneconfig/OneConfig.java
+++ b/src/main/java/cc/polyfrost/oneconfig/OneConfig.java
@@ -12,6 +12,7 @@ import cc.polyfrost.oneconfig.lwjgl.RenderManager;
import cc.polyfrost.oneconfig.lwjgl.font.Fonts;
import cc.polyfrost.oneconfig.lwjgl.image.Images;
import cc.polyfrost.oneconfig.test.TestConfig;
+import cc.polyfrost.oneconfig.utils.hypixel.HypixelUtils;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.client.ClientCommandHandler;
import net.minecraftforge.fml.common.DummyModContainer;
@@ -50,7 +51,8 @@ public class OneConfig {
BlurHandler.INSTANCE.load();
testConfig = new TestConfig();
ClientCommandHandler.instance.registerCommand(new OneConfigCommand());
- EventManager.INSTANCE.getEventBus().register(new HudCore());
+ EventManager.INSTANCE.register(new HudCore());
+ EventManager.INSTANCE.register(HypixelUtils.INSTANCE);
RenderManager.setupAndDraw((vg) -> {
RenderManager.drawRoundedRect(vg, -100, -100, 50, 50, -1, 12f);
RenderManager.drawString(vg, "OneConfig loading...", -100, -100, -1, 12f, Fonts.MEDIUM);
diff --git a/src/main/java/cc/polyfrost/oneconfig/events/EventManager.java b/src/main/java/cc/polyfrost/oneconfig/events/EventManager.java
index 956c9b6..e1c1427 100644
--- a/src/main/java/cc/polyfrost/oneconfig/events/EventManager.java
+++ b/src/main/java/cc/polyfrost/oneconfig/events/EventManager.java
@@ -14,4 +14,16 @@ public final class EventManager {
public EventBus getEventBus() {
return eventBus;
}
+
+ public void register(Object object) {
+ eventBus.register(object);
+ }
+
+ public void unregister(Object object) {
+ eventBus.unregister(object);
+ }
+
+ public void post(Object event) {
+ eventBus.post(event);
+ }
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java b/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java
new file mode 100644
index 0000000..b69b89b
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java
@@ -0,0 +1,12 @@
+package cc.polyfrost.oneconfig.events.event;
+
+
+import net.minecraft.util.IChatComponent;
+
+public class ChatReceiveEvent extends CancellableEvent {
+ public final IChatComponent message;
+
+ public ChatReceiveEvent(IChatComponent message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/events/event/LocrawEvent.java b/src/main/java/cc/polyfrost/oneconfig/events/event/LocrawEvent.java
new file mode 100644
index 0000000..42565f7
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/events/event/LocrawEvent.java
@@ -0,0 +1,11 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import cc.polyfrost.oneconfig.utils.hypixel.LocrawInfo;
+
+public class LocrawEvent {
+ public final LocrawInfo info;
+
+ public LocrawEvent(LocrawInfo info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/events/event/WorldLoadEvent.java b/src/main/java/cc/polyfrost/oneconfig/events/event/WorldLoadEvent.java
new file mode 100644
index 0000000..1583fe6
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/events/event/WorldLoadEvent.java
@@ -0,0 +1,4 @@
+package cc.polyfrost.oneconfig.events.event;
+
+public class WorldLoadEvent {
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/gui/pages/CreditsPage.java b/src/main/java/cc/polyfrost/oneconfig/gui/pages/CreditsPage.java
index d6c00ab..e41cc9e 100644
--- a/src/main/java/cc/polyfrost/oneconfig/gui/pages/CreditsPage.java
+++ b/src/main/java/cc/polyfrost/oneconfig/gui/pages/CreditsPage.java
@@ -29,7 +29,8 @@ public class CreditsPage extends Page {
RenderManager.drawString(vg, " - NanoVG (memononen) - NanoVG Library", x + 20, y + 370, -1, 12, Fonts.REGULAR);
RenderManager.drawString(vg, " - UniversalCraft (Sk1er LLC) - Multiversioning bindings", x + 20, y + 385, -1, 12, Fonts.REGULAR);
RenderManager.drawString(vg, " - Easing Functions (jesusgollonet)", x + 20, y + 400, -1, 12, Fonts.REGULAR);
- RenderManager.drawString(vg, " - Quiltflower (W-OVERFLOW) - Gradle toolkit", x + 20, y + 415, -1, 12, Fonts.REGULAR);
+ RenderManager.drawString(vg, " - Quiltflower (Quilt Team) - Gradle decompiler", x + 20, y + 415, -1, 12, Fonts.REGULAR);
+ RenderManager.drawString(vg, " - Seraph (Scherso) - Locraw and Multithreading utilities", x + 20, y + 430, -1, 12, Fonts.REGULAR);
RenderManager.drawString(vg, "Terms Of Service & Licensing", x + 20, y + 517, -1, 24, Fonts.SEMIBOLD);
RenderManager.drawString(vg, " - License Summary goes here", x + 20, y + 540, -1, 12, Fonts.REGULAR);
diff --git a/src/main/java/cc/polyfrost/oneconfig/lwjgl/BlurHandler.java b/src/main/java/cc/polyfrost/oneconfig/lwjgl/BlurHandler.java
index a769e12..d2b4811 100644
--- a/src/main/java/cc/polyfrost/oneconfig/lwjgl/BlurHandler.java
+++ b/src/main/java/cc/polyfrost/oneconfig/lwjgl/BlurHandler.java
@@ -45,16 +45,16 @@ public class BlurHandler {
* Simply initializes the blur mod so events are properly handled by forge.
*/
public void load() {
- EventManager.INSTANCE.getEventBus().register(this);
+ EventManager.INSTANCE.register(this);
}
@Subscribe
- public void onGuiChange(ScreenOpenEvent event) {
+ private void onGuiChange(ScreenOpenEvent event) {
reloadBlur(event.screen);
}
@Subscribe
- public void onRenderTick(RenderEvent event) {
+ private void onRenderTick(RenderEvent event) {
if (event.stage != Stage.END) {
return;
}
@@ -113,7 +113,7 @@ public class BlurHandler {
* one of many conditions are met, such as no current other shader
* is being used, we actually have the blur setting enabled
*/
- public void reloadBlur(GuiScreen gui) {
+ private void reloadBlur(GuiScreen gui) {
// Don't do anything if no world is loaded
if (UMinecraft.getWorld() == null) {
return;
diff --git a/src/main/java/cc/polyfrost/oneconfig/mixin/GuiIngameForgeMixin.java b/src/main/java/cc/polyfrost/oneconfig/mixin/GuiIngameForgeMixin.java
index 1774fdc..36ec90b 100644
--- a/src/main/java/cc/polyfrost/oneconfig/mixin/GuiIngameForgeMixin.java
+++ b/src/main/java/cc/polyfrost/oneconfig/mixin/GuiIngameForgeMixin.java
@@ -8,10 +8,10 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-@Mixin(GuiIngameForge.class)
+@Mixin(value = GuiIngameForge.class, remap = false)
public class GuiIngameForgeMixin {
- @Inject(method = "renderGameOverlay", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/GuiIngameForge;post(Lnet/minecraftforge/client/event/RenderGameOverlayEvent$ElementType;)V", shift = At.Shift.AFTER))
+ @Inject(method = "renderGameOverlay", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/GuiIngameForge;post(Lnet/minecraftforge/client/event/RenderGameOverlayEvent$ElementType;)V", shift = At.Shift.AFTER, remap = false), remap = true)
private void onRenderGameOverlay(float partialTicks, CallbackInfo ci) {
- EventManager.INSTANCE.getEventBus().post(new HudRenderEvent(partialTicks));
+ EventManager.INSTANCE.post(new HudRenderEvent(partialTicks));
}
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/mixin/MinecraftMixin.java b/src/main/java/cc/polyfrost/oneconfig/mixin/MinecraftMixin.java
index 05d2819..97b843a 100644
--- a/src/main/java/cc/polyfrost/oneconfig/mixin/MinecraftMixin.java
+++ b/src/main/java/cc/polyfrost/oneconfig/mixin/MinecraftMixin.java
@@ -19,30 +19,30 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MinecraftMixin {
@Shadow private Timer timer;
- @Inject(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onRenderTickStart(F)V", shift = At.Shift.AFTER))
+ @Inject(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onRenderTickStart(F)V", shift = At.Shift.AFTER, remap = false), remap = true)
private void onRenderTickStart(CallbackInfo ci) {
- EventManager.INSTANCE.getEventBus().post(new RenderEvent(Stage.START, timer.renderPartialTicks));
+ EventManager.INSTANCE.post(new RenderEvent(Stage.START, timer.renderPartialTicks));
}
- @Inject(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onRenderTickEnd(F)V", shift = At.Shift.AFTER))
+ @Inject(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onRenderTickEnd(F)V", shift = At.Shift.AFTER, remap = false), remap = true)
private void onRenderTickEnd(CallbackInfo ci) {
- EventManager.INSTANCE.getEventBus().post(new RenderEvent(Stage.END, timer.renderPartialTicks));
+ EventManager.INSTANCE.post(new RenderEvent(Stage.END, timer.renderPartialTicks));
}
- @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onPreClientTick()V", shift = At.Shift.AFTER))
+ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onPreClientTick()V", shift = At.Shift.AFTER, remap = false), remap = true)
private void onClientTickStart(CallbackInfo ci) {
- EventManager.INSTANCE.getEventBus().post(new TickEvent(Stage.START));
+ EventManager.INSTANCE.post(new TickEvent(Stage.START));
}
- @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onPostClientTick()V", shift = At.Shift.AFTER))
+ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;onPostClientTick()V", shift = At.Shift.AFTER, remap = false), remap = true)
private void onClientTickEnd(CallbackInfo ci) {
- EventManager.INSTANCE.getEventBus().post(new TickEvent(Stage.END));
+ EventManager.INSTANCE.post(new TickEvent(Stage.END));
}
- @ModifyExpressionValue(method = "displayGuiScreen", at = @At(value = "NEW", target = "Lnet/minecraftforge/client/event/GuiOpenEvent;<init>(Lnet/minecraft/client/gui/GuiScreen;)V"))
+ @ModifyExpressionValue(method = "displayGuiScreen", at = @At(value = "NEW", target = "Lnet/minecraftforge/client/event/GuiOpenEvent;<init>(Lnet/minecraft/client/gui/GuiScreen;)V", remap = false), remap = true)
private GuiOpenEvent onGuiOpenEvent(GuiOpenEvent screen) {
ScreenOpenEvent event = new ScreenOpenEvent(screen.gui);
- EventManager.INSTANCE.getEventBus().post(event);
+ EventManager.INSTANCE.post(event);
if (event.isCancelled) {
screen.setCanceled(true);
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/mixin/NetHandlerPlayClientMixin.java b/src/main/java/cc/polyfrost/oneconfig/mixin/NetHandlerPlayClientMixin.java
new file mode 100644
index 0000000..c7056d5
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/mixin/NetHandlerPlayClientMixin.java
@@ -0,0 +1,24 @@
+package cc.polyfrost.oneconfig.mixin;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.ChatReceiveEvent;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.network.play.server.S02PacketChat;
+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;
+
+@Mixin(NetHandlerPlayClient.class)
+public class NetHandlerPlayClientMixin {
+ @Inject(method = "handleChat", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/event/ForgeEventFactory;onClientChat(BLnet/minecraft/util/IChatComponent;)Lnet/minecraft/util/IChatComponent;", remap = false), cancellable = true, remap = true)
+ private void onClientChat(S02PacketChat packetIn, CallbackInfo ci) {
+ if (packetIn.getType() == 0) {
+ ChatReceiveEvent event = new ChatReceiveEvent(packetIn.getChatComponent());
+ EventManager.INSTANCE.post(event);
+ if (event.isCancelled) {
+ ci.cancel();
+ }
+ }
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/mixin/WorldClientMixin.java b/src/main/java/cc/polyfrost/oneconfig/mixin/WorldClientMixin.java
new file mode 100644
index 0000000..13a3821
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/mixin/WorldClientMixin.java
@@ -0,0 +1,21 @@
+package cc.polyfrost.oneconfig.mixin;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.WorldLoadEvent;
+import net.minecraft.client.multiplayer.WorldClient;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.profiler.Profiler;
+import net.minecraft.world.EnumDifficulty;
+import net.minecraft.world.WorldSettings;
+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;
+
+@Mixin(WorldClient.class)
+public class WorldClientMixin {
+ @Inject(method = "<init>", at = @At("RETURN"))
+ private void onWorldLoad(NetHandlerPlayClient p_i45063_1_, WorldSettings p_i45063_2_, int p_i45063_3_, EnumDifficulty p_i45063_4_, Profiler p_i45063_5_, CallbackInfo ci) {
+ EventManager.INSTANCE.post(new WorldLoadEvent());
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/plugin/asm/tweakers/VigilantTransformer.java b/src/main/java/cc/polyfrost/oneconfig/plugin/asm/tweakers/VigilantTransformer.java
index 45dc1e2..fecdeed 100644
--- a/src/main/java/cc/polyfrost/oneconfig/plugin/asm/tweakers/VigilantTransformer.java
+++ b/src/main/java/cc/polyfrost/oneconfig/plugin/asm/tweakers/VigilantTransformer.java
@@ -93,12 +93,11 @@ public class VigilantTransformer implements ITransformer {
public static VigilanceConfig returnNewConfig(Vigilant vigilant, File file) {
if (vigilant != null && Minecraft.getMinecraft().isCallingFromMinecraftThread()) {
String name = !vigilant.getGuiTitle().equals("Settings") ? vigilant.getGuiTitle() : Loader.instance().activeModContainer() == null ? "Unknown" : Loader.instance().activeModContainer().getName();
- String imageName = Loader.instance().activeModContainer() == null || Loader.instance().activeModContainer().getMetadata().logoFile.trim().equals("") ? null : "/" + Loader.instance().activeModContainer().getMetadata().logoFile;
if (name.equals("OneConfig")) name = "Essential";
String finalName = name;
// duplicate fix
if (ConfigCore.oneConfigMods.stream().anyMatch(mod -> mod.name.equals(finalName))) return null;
- return new VigilanceConfig(new Mod(name, ModType.THIRD_PARTY, imageName), file.getAbsolutePath(), vigilant);
+ return new VigilanceConfig(new Mod(name, ModType.THIRD_PARTY), file.getAbsolutePath(), vigilant);
} else {
return null;
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/IOUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/IOUtils.java
index 64cabfd..efb0c16 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/IOUtils.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/IOUtils.java
@@ -10,6 +10,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
+/**
+ * Utility class for I/O operations.
+ */
public final class IOUtils {
/**
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/InputUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/InputUtils.java
index 86e6c86..0ba4c00 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/InputUtils.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/InputUtils.java
@@ -25,27 +25,78 @@ public final class InputUtils {
return mouseX > x && mouseY > y && mouseX < x + width && mouseY < y + height;
}
+ /**
+ * Checks whether the mouse is currently over a specific region and clicked.
+ *
+ * @param x the x position of the region
+ * @param y the y position of the region
+ * @param width the width of the region
+ * @param height the height of the region
+ * @param ignoreBlock if true, will ignore {@link InputUtils#blockClicks(boolean)}
+ * @return true if the mouse is clicked and is over the region, false if not
+ * @see InputUtils#isAreaHovered(int, int, int, int)
+ */
public static boolean isAreaClicked(int x, int y, int width, int height, boolean ignoreBlock) {
return isAreaHovered(x, y, width, height) && isClicked(ignoreBlock);
}
+ /**
+ * Checks whether the mouse is currently over a specific region and clicked.
+ *
+ * @param x the x position of the region
+ * @param y the y position of the region
+ * @param width the width of the region
+ * @param height the height of the region
+ * @return true if the mouse is clicked and is over the region, false if not
+ * @see InputUtils#isAreaClicked(int, int, int, int, boolean)
+ */
public static boolean isAreaClicked(int x, int y, int width, int height) {
return isAreaClicked(x, y, width, height, false);
}
+ /**
+ * Checks whether the mouse is clicked or not.
+ *
+ * @param ignoreBlock if true, will ignore {@link InputUtils#blockClicks(boolean)}
+ * @return true if the mouse is clicked, false if not
+ */
public static boolean isClicked(boolean ignoreBlock) {
return OneConfigGui.INSTANCE != null && OneConfigGui.INSTANCE.mouseDown && !Mouse.isButtonDown(0) && (!blockClicks || ignoreBlock);
}
+ /**
+ * Checks whether the mouse is clicked or not.
+ *
+ * @return true if the mouse is clicked, false if not
+ * @see InputUtils#isClicked(boolean)
+ */
public static boolean isClicked() {
return isClicked(false);
}
+ /**
+ * Gets the current mouse X position.
+ * <p>
+ * All values returned from this class are not scaled to Minecraft's GUI scale.
+ * For scaled values, see {@link cc.polyfrost.oneconfig.libs.universal.UMouse}.
+ * </p>
+ *
+ * @return the current mouse X position
+ */
public static int mouseX() {
if (OneConfigGui.INSTANCE == null) return Mouse.getX();
return (int) (Mouse.getX() / OneConfigGui.INSTANCE.getScaleFactor());
}
+ /**
+ * Gets the current mouse Y position.
+ * <p>
+ * All values returned from this class are not scaled to Minecraft's GUI scale.
+ * For scaled values, see {@link cc.polyfrost.oneconfig.libs.universal.UMouse}.
+ * </p>
+ *
+ * @return the current mouse Y position
+ */
public static int mouseY() {
if (OneConfigGui.INSTANCE == null) return UResolution.getWindowHeight() - Math.abs(Mouse.getY());
return (int) ((UResolution.getWindowHeight() - Math.abs(Mouse.getY())) / OneConfigGui.INSTANCE.getScaleFactor());
@@ -58,6 +109,10 @@ public final class InputUtils {
blockClicks = value;
}
+ /**
+ * Whether clicks are blocked
+ * @return true if clicks are blocked, false if not
+ */
public static boolean isBlockingClicks() {
return blockClicks;
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/JsonUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/JsonUtils.java
index 4ea9ce2..67881e9 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/JsonUtils.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/JsonUtils.java
@@ -3,9 +3,24 @@ package cc.polyfrost.oneconfig.utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
+/**
+ * Various utility methods for working with JSON.
+ */
public final class JsonUtils {
+
+ /**
+ * The instance of the parser.
+ */
public static final JsonParser PARSER = new JsonParser();
+ /**
+ * Parses a string into a {@link JsonElement}.
+ *
+ * @param string The string to parse.
+ * @param catchExceptions Whether to catch exceptions.
+ * @return The {@link JsonElement}.
+ * @see JsonParser#parse(String)
+ */
public static JsonElement parseString(String string, boolean catchExceptions) {
try {
return PARSER.parse(string);
@@ -18,6 +33,13 @@ public final class JsonUtils {
}
}
+ /**
+ * Parses a string into a {@link JsonElement}.
+ *
+ * @param string The string to parse.
+ * @return The {@link JsonElement}.
+ * @see JsonUtils#parseString(String, boolean)
+ */
public static JsonElement parseString(String string) {
return parseString(string, true);
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/Multithreading.java b/src/main/java/cc/polyfrost/oneconfig/utils/Multithreading.java
new file mode 100644
index 0000000..518d699
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/Multithreading.java
@@ -0,0 +1,75 @@
+package cc.polyfrost.oneconfig.utils;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.*;
+
+/**
+ * Allows for easy multithreading.
+ * <p>
+ * Taken from Seraph by Scherso under LGPL-2.1
+ * <a href="https://github.com/Scherso/Seraph/blob/master/LICENSE">https://github.com/Scherso/Seraph/blob/master/LICENSE</a>
+ * </p>
+ */
+public class Multithreading {
+ private static final ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("OneConfig-%d").build());
+ private static final ScheduledExecutorService runnableExecutor = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1);
+
+ /**
+ * Runs the runnable asynchronously.
+ *
+ * @param runnable The runnable to run.
+ * @see Multithreading#submit(Runnable)
+ */
+ public static void runAsync(Runnable runnable) {
+ submit(runnable);
+ }
+
+ /**
+ * Runs the provided runnables asynchronously.
+ *
+ * @param runnables The runnables to run.
+ * @see Multithreading#runAsync(Runnable)
+ */
+ public static void runAsync(Runnable... runnables) {
+ for (Runnable runnable : runnables) {
+ runAsync(runnable);
+ }
+ }
+
+ /**
+ * Submits the Runnable to the executor, making it run asynchronously.
+ *
+ * @param runnable The runnable to run.
+ * @return The future representing the submitted runnable.
+ * @see ExecutorService#submit(Runnable)
+ */
+ public static Future<?> submit(Runnable runnable) {
+ return executorService.submit(runnable);
+ }
+
+ /**
+ * Schedules the runnable to run asynchronously after the specified delay.
+ *
+ * @param runnable The runnable to run.
+ * @param delay The delay before the runnable is run.
+ * @param timeUnit The {@link TimeUnit} of the delay.
+ * @see Multithreading#submitScheduled(Runnable, long, TimeUnit)
+ */
+ public static void schedule(Runnable runnable, long delay, TimeUnit timeUnit) {
+ submitScheduled(runnable, delay, timeUnit);
+ }
+
+ /**
+ * Submits the Runnable to the executor after a delay, making it run asynchronously.
+ *
+ * @param runnable The runnable to run.
+ * @param delay The delay before the runnable is run.
+ * @param timeUnit The {@link TimeUnit} of the delay.
+ * @return The future representing the submitted runnable.
+ * @see ScheduledExecutorService#schedule(Runnable, long, TimeUnit)
+ */
+ public static ScheduledFuture<?> submitScheduled(Runnable runnable, long delay, TimeUnit timeUnit) {
+ return runnableExecutor.schedule(runnable, delay, timeUnit);
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/NetworkUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/NetworkUtils.java
index 34d4382..767f36f 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/NetworkUtils.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/NetworkUtils.java
@@ -11,18 +11,19 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
+/**
+ * Utility class for accessing the internet.
+ */
public final class NetworkUtils {
- private static InputStream setupConnection(String url, String userAgent, int timeout, boolean useCaches) throws IOException {
- HttpURLConnection connection = ((HttpURLConnection) new URL(url).openConnection());
- connection.setRequestMethod("GET");
- connection.setUseCaches(useCaches);
- connection.addRequestProperty("User-Agent", userAgent);
- connection.setReadTimeout(timeout);
- connection.setConnectTimeout(timeout);
- connection.setDoOutput(true);
- return connection.getInputStream();
- }
+ /**
+ * Gets the contents of a URL as a String.
+ * @param url The URL to read.
+ * @param userAgent The user agent to use.
+ * @param timeout The timeout in milliseconds.
+ * @param useCaches Whether to use caches.
+ * @return The contents of the URL.
+ */
public static String getString(String url, String userAgent, int timeout, boolean useCaches) {
try (InputStreamReader input = new InputStreamReader(setupConnection(url, userAgent, timeout, useCaches), StandardCharsets.UTF_8)) {
return IOUtils.toString(input);
@@ -32,23 +33,52 @@ public final class NetworkUtils {
}
}
+ /**
+ * Gets the contents of a URL as a String.
+ *
+ * @param url The URL to read.
+ * @return The contents of the URL.
+ * @see NetworkUtils#getString(String, String, int, boolean)
+ */
public static String getString(String url) {
return getString(url, "OneConfig/1.0.0", 5000, false);
}
+ /**
+ * Gets the contents of a URL as a JsonElement.
+ *
+ * @param url The URL to read.
+ * @param userAgent The user agent to use.
+ * @param timeout The timeout in milliseconds.
+ * @param useCaches Whether to use caches.
+ * @return The contents of the URL.
+ * @see NetworkUtils#getString(String, String, int, boolean)
+ * @see JsonUtils#parseString(String)
+ */
public static JsonElement getJsonElement(String url, String userAgent, int timeout, boolean useCaches) {
return JsonUtils.parseString(getString(url, userAgent, timeout, useCaches));
}
+ /**
+ * Gets the contents of a URL as a JsonElement.
+ *
+ * @param url The URL to read.
+ * @return The contents of the URL.
+ * @see NetworkUtils#getJsonElement(String, String, int, boolean)
+ */
public static JsonElement getJsonElement(String url) {
return getJsonElement(url, "OneConfig/1.0.0", 5000, false);
}
-
- public static boolean downloadFile(String url, File file) {
- return downloadFile(url, file, "OneConfig/1.0.0", 5000, false);
- }
-
+ /**
+ * Downloads a file from a URL.
+ * @param url The URL to download from.
+ * @param file The file to download to.
+ * @param userAgent The user agent to use.
+ * @param timeout The timeout in milliseconds.
+ * @param useCaches Whether to use caches.
+ * @return Whether the download was successful.
+ */
public static boolean downloadFile(String url, File file, String userAgent, int timeout, boolean useCaches) {
url = url.replace(" ", "%20");
try (FileOutputStream fileOut = new FileOutputStream(file); BufferedInputStream in = new BufferedInputStream(setupConnection(url, userAgent, timeout, useCaches))) {
@@ -60,8 +90,24 @@ public final class NetworkUtils {
return true;
}
- public static String getFileChecksum(String filename) {
- try (FileInputStream inputStream = new FileInputStream(filename)) {
+ /**
+ * Downloads a file from a URL.
+ * @param url The URL to download from.
+ * @param file The file to download to.
+ * @return Whether the download was successful.
+ * @see NetworkUtils#downloadFile(String, File, String, int, boolean)
+ */
+ public static boolean downloadFile(String url, File file) {
+ return downloadFile(url, file, "OneConfig/1.0.0", 5000, false);
+ }
+
+ /**
+ * Gets the SHA-256 hash of a file.
+ * @param file The file to hash.
+ * @return The SHA-256 hash of the file.
+ */
+ public static String getFileChecksum(File file) {
+ try (FileInputStream inputStream = new FileInputStream(file)) {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] bytesBuffer = new byte[1024];
int bytesRead;
@@ -77,6 +123,28 @@ public final class NetworkUtils {
return "";
}
+ /**
+ * Launches a URL in the default browser.
+ *
+ * @param uri The URI to launch.
+ * @see UDesktop#browse(URI)
+ * @see java.awt.Desktop#browse(URI)
+ */
+ public static void browseLink(String uri) {
+ UDesktop.browse(URI.create(uri));
+ }
+
+ private static InputStream setupConnection(String url, String userAgent, int timeout, boolean useCaches) throws IOException {
+ HttpURLConnection connection = ((HttpURLConnection) new URL(url).openConnection());
+ connection.setRequestMethod("GET");
+ connection.setUseCaches(useCaches);
+ connection.addRequestProperty("User-Agent", userAgent);
+ connection.setReadTimeout(timeout);
+ connection.setConnectTimeout(timeout);
+ connection.setDoOutput(true);
+ return connection.getInputStream();
+ }
+
private static String convertByteArrayToHexString(byte[] arrayBytes) {
StringBuilder stringBuffer = new StringBuilder();
for (byte arrayByte : arrayBytes) {
@@ -85,8 +153,4 @@ public final class NetworkUtils {
}
return stringBuffer.toString();
}
-
- public static void browseLink(String uri) {
- UDesktop.browse(URI.create(uri));
- }
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java
index 252a588..f9f0fbd 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java
@@ -5,7 +5,20 @@ import cc.polyfrost.oneconfig.lwjgl.font.Fonts;
import java.util.ArrayList;
+/**
+ * Simple text utility class for NanoVG text rendering.
+ */
public final class TextUtils {
+
+ /**
+ * Wraps a string into an array of lines.
+ * @param vg The NanoVG context.
+ * @param text The text to wrap.
+ * @param maxWidth The maximum width of each line.
+ * @param fontSize The font size.
+ * @param font The font to use.
+ * @return The array of lines.
+ */
public static ArrayList<String> wrapText(long vg, String text, float maxWidth, float fontSize, Fonts font) {
ArrayList<String> wrappedText = new ArrayList<>();
text += " ";
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/TickDelay.java b/src/main/java/cc/polyfrost/oneconfig/utils/TickDelay.java
index 2fa2017..35b7b8b 100644
--- a/src/main/java/cc/polyfrost/oneconfig/utils/TickDelay.java
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/TickDelay.java
@@ -5,23 +5,26 @@ import cc.polyfrost.oneconfig.events.event.Stage;
import cc.polyfrost.oneconfig.events.event.TickEvent;
import cc.polyfrost.oneconfig.libs.eventbus.Subscribe;
+/**
+ * Schedules a Runnable to be called after a certain amount of ticks.
+ */
public class TickDelay {
private int delay;
private final Runnable function;
public TickDelay(Runnable functionName, int ticks) {
- EventManager.INSTANCE.getEventBus().register(this);
+ EventManager.INSTANCE.register(this);
delay = ticks;
function = functionName;
}
@Subscribe
- public void onTick(TickEvent event) {
+ protected void onTick(TickEvent event) {
if (event.stage == Stage.START) {
// Delay expired
if (delay < 1) {
function.run();
- EventManager.INSTANCE.getEventBus().unregister(this);
+ EventManager.INSTANCE.unregister(this);
}
delay--;
}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/HypixelUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/HypixelUtils.java
new file mode 100644
index 0000000..d7a9b0d
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/HypixelUtils.java
@@ -0,0 +1,159 @@
+package cc.polyfrost.oneconfig.utils.hypixel;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.*;
+import cc.polyfrost.oneconfig.libs.eventbus.Subscribe;
+import cc.polyfrost.oneconfig.libs.universal.UChat;
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.libs.universal.wrappers.UPlayer;
+import cc.polyfrost.oneconfig.libs.universal.wrappers.message.UTextComponent;
+import cc.polyfrost.oneconfig.utils.JsonUtils;
+import cc.polyfrost.oneconfig.utils.Multithreading;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Various utilities for Hypixel.
+ * <p>
+ * Locraw utilities taken from Seraph by Scherso under LGPL-2.1
+ * <a href="https://github.com/Scherso/Seraph/blob/master/LICENSE">https://github.com/Scherso/Seraph/blob/master/LICENSE</a>
+ * </p>
+ */
+public class HypixelUtils {
+ public static final HypixelUtils INSTANCE = new HypixelUtils();
+ private final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+ private int tick = 0;
+ private int limboLoop = 0;
+
+ private boolean sentCommand = false;
+ private boolean sendPermitted = false;
+ private boolean inGame;
+
+ private LocrawInfo locraw;
+ private LocrawInfo previousLocraw;
+
+ /**
+ * Checks whether the player is on Hypixel.
+ *
+ * @return Whether the player is on Hypixel.
+ * @see <a href="https://canary.discord.com/channels/864592657572560958/945075920664928276/978649312013725747">this discord link from jade / asbyth</a>
+ */
+ public boolean isHypixel() {
+ if (UMinecraft.getWorld() == null || UMinecraft.getMinecraft().isIntegratedServerRunning()) return false;
+
+ net.minecraft.client.entity.EntityPlayerSP player = UPlayer.getPlayer();
+ if (player == null) return false;
+ String serverBrand = player.getClientBrand();
+
+ if (serverBrand == null) return false;
+
+ return serverBrand.toLowerCase(Locale.ENGLISH).contains("hypixel");
+ }
+
+ /**
+ * Queues a locraw update after the specified interval.
+ *
+ * @param interval The interval in milliseconds.
+ */
+ public void queueUpdate(long interval) {
+ sendPermitted = true;
+ Multithreading.schedule(() -> {
+ if (sendPermitted) {
+ UChat.say("/locraw");
+ }
+ }, interval, TimeUnit.MILLISECONDS);
+ }
+
+ @Subscribe
+ private void onTick(TickEvent event) {
+ if (event.stage == Stage.START) {
+ tick++;
+
+ if (tick % 20 == 0) {
+ tick = 0;
+ if (isHypixel() && !sentCommand) {
+ queueUpdate(500);
+ sentCommand = true;
+ }
+ }
+ }
+ }
+
+ @Subscribe
+ private void onWorldLoad(WorldLoadEvent event) {
+ locraw = null;
+ sendPermitted = false;
+ sentCommand = false;
+ limboLoop = 0;
+ }
+
+ @Subscribe
+ private void onMessageReceived(ChatReceiveEvent event) {
+ if (!sentCommand) return;
+ try {
+ final String msg = UTextComponent.Companion.stripFormatting(event.message.getUnformattedText());
+ // Checking for rate limitation.
+ if (!(msg.startsWith("{") && msg.endsWith("}"))) {
+ if (msg.contains("You are sending too many commands! Please try again in a few seconds.")) // if you're being rate limited, the /locraw command will be resent in 5 seconds.
+ queueUpdate(5000);
+ return;
+ }
+
+ JsonElement raw = JsonUtils.parseString(msg);
+ if (!raw.isJsonObject()) return;
+ JsonObject json = raw.getAsJsonObject();
+ LocrawInfo parsed = GSON.fromJson(json, LocrawInfo.class);
+
+ if (5 > limboLoop && parsed.getGameType() == LocrawInfo.GameType.LIMBO) {
+ sentCommand = false;
+ limboLoop++;
+ queueUpdate(1000);
+ } else locraw = parsed; // if the player isn't in limbo, the parsed info is used.
+
+ if (locraw != null) {
+ locraw.setGameType(LocrawInfo.GameType.getFromLocraw(this.locraw.getRawGameType()));
+ if (parsed.getGameMode().equals("lobby")) {
+ inGame = false; // If your gamemode returns "lobby", boolean inGame is false.
+ } else {
+ previousLocraw = parsed;
+ inGame = true; // If your gamemode does not return "lobby", boolean inGame is true.
+ }
+ EventManager.INSTANCE.post(new LocrawEvent(locraw));
+ event.isCancelled = true;
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns whether the player is in game.
+ * @return Whether the player is in game.
+ */
+ public boolean isInGame() {
+ return this.inGame;
+ }
+
+ /**
+ * Returns the current {@link LocrawInfo}.
+ * @return The current {@link LocrawInfo}.
+ * @see LocrawInfo
+ */
+ public LocrawInfo getLocrawInfo() {
+ return this.locraw;
+ }
+
+ /**
+ * Returns the previous {@link LocrawInfo}.
+ * @return The previous {@link LocrawInfo}.
+ * @see LocrawInfo
+ */
+ public LocrawInfo getPreviousLocraw() {
+ return this.previousLocraw;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/LocrawInfo.java b/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/LocrawInfo.java
new file mode 100644
index 0000000..4a946f0
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/utils/hypixel/LocrawInfo.java
@@ -0,0 +1,110 @@
+package cc.polyfrost.oneconfig.utils.hypixel;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.Objects;
+
+/**
+ * Represents the location of the player in Hypixel.
+ * <p>
+ * Locraw utilities taken from Seraph by Scherso under LGPL-2.1
+ * <a href="https://github.com/Scherso/Seraph/blob/master/LICENSE">https://github.com/Scherso/Seraph/blob/master/LICENSE</a>
+ * </p>
+ *
+ * @see HypixelUtils
+ */
+public class LocrawInfo {
+ @SerializedName("server")
+ private String serverId;
+ @SerializedName("mode")
+ private String gameMode = "lobby";
+ @SerializedName("map")
+ private String mapName;
+ @SerializedName("gametype")
+ private String rawGameType;
+ private GameType gameType;
+
+ /**
+ * @return The serverID of the server you are currently on, ex: mini121
+ */
+ public String getServerId() {
+ return serverId;
+ }
+
+ /**
+ * @return The GameType of the server as a String.
+ */
+ public String getRawGameType() {
+ return rawGameType;
+ }
+
+ /**
+ * @return The GameMode of the server, ex: solo_insane
+ */
+ public String getGameMode() {
+ return gameMode;
+ }
+
+ /**
+ * @return The GameType of the server as an Enum.
+ */
+ public GameType getGameType() {
+ return gameType;
+ }
+
+ /**
+ * @param gameType The GameType to set it to.
+ */
+ public void setGameType(GameType gameType) {
+ this.gameType = gameType;
+ }
+
+ /**
+ * @return The map of the server, ex: Shire.
+ */
+ public String getMapName() {
+ return mapName;
+ }
+
+ @Override
+ public String toString() {
+ return "LocrawInfo{" + "serverId='" + serverId + '\'' + ", gameMode='" + gameMode + '\'' + ", mapName='" + mapName + '\'' + ", rawGameType='" + rawGameType + '\'' + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ LocrawInfo that = (LocrawInfo) o;
+ return Objects.equals(serverId, that.serverId) && Objects.equals(gameMode, that.gameMode) && Objects.equals(mapName, that.mapName) && Objects.equals(rawGameType, that.rawGameType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(serverId, gameMode, mapName, rawGameType);
+ }
+
+ public enum GameType {
+ UNKNOWN(""), LIMBO("LIMBO"), BEDWARS("BEDWARS"), SKYWARS("SKYWARS"), PROTOTYPE("PROTOTYPE"), SKYBLOCK("SKYBLOCK"), MAIN("MAIN"), MURDER_MYSTERY("MURDER_MYSTERY"), HOUSING("HOUSING"), ARCADE_GAMES("ARCADE"), BUILD_BATTLE("BUILD_BATTLE"), DUELS("DUELS"), PIT("PIT"), UHC_CHAMPIONS("UHC"), SPEED_UHC("SPEED_UHC"), TNT_GAMES("TNTGAMES"), CLASSIC_GAMES("LEGACY"), COPS_AND_CRIMS("MCGO"), BLITZ_SG("SURVIVAL_GAMES"), MEGA_WALLS("WALLS3"), SMASH_HEROES("SUPER_SMASH"), WARLORDS("BATTLEGROUND");
+
+ private final String serverName;
+
+ GameType(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public static GameType getFromLocraw(String gameType) {
+ for (GameType value : values()) {
+ if (value.serverName.equals(gameType)) {
+ return value;
+ }
+ }
+
+ return UNKNOWN;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+ }
+}
diff --git a/src/main/resources/mixins.oneconfig.json b/src/main/resources/mixins.oneconfig.json
index f90b37c..86e61e1 100644
--- a/src/main/resources/mixins.oneconfig.json
+++ b/src/main/resources/mixins.oneconfig.json
@@ -11,6 +11,8 @@
"client": [
"GuiIngameForgeMixin",
"MinecraftMixin",
- "ShaderGroupAccessor"
+ "NetHandlerPlayClientMixin",
+ "ShaderGroupAccessor",
+ "WorldClientMixin"
]
} \ No newline at end of file