aboutsummaryrefslogtreecommitdiff
path: root/versions/src/main/java/cc/polyfrost/oneconfig
diff options
context:
space:
mode:
Diffstat (limited to 'versions/src/main/java/cc/polyfrost/oneconfig')
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java22
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/events/event/ReceivePacketEvent.java11
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/events/event/ScreenOpenEvent.java17
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/events/event/SendPacketEvent.java11
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/events/event/TimerUpdateEvent.java24
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/OneConfig.java93
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/gui/impl/BlurHandlerImpl.java132
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/GuiIngameForgeMixin.java19
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/MinecraftMixin.java100
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetHandlerPlayClientMixin.java38
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetworkManagerMixin.java25
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/OptifineConfigMixin.java23
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/ShaderGroupAccessor.java16
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/VigilantMixin.java10
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/WorldClientMixin.java23
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java102
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java24
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java117
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java47
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java89
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java47
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java22
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java26
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GLPlatformImpl.java67
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GuiPlatformImpl.java31
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl.java13
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java26
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl.java53
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java12
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl.java22
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/utils/commands/PlatformCommandManagerImpl.java330
-rw-r--r--versions/src/main/java/cc/polyfrost/oneconfig/utils/gui/GuiUtils.java100
32 files changed, 1692 insertions, 0 deletions
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java
new file mode 100644
index 0000000..6585729
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ChatReceiveEvent.java
@@ -0,0 +1,22 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import cc.polyfrost.oneconfig.libs.universal.wrappers.message.UTextComponent;
+import net.minecraft.util.IChatComponent;
+
+/**
+ * Called when a chat message is received.
+ */
+public class ChatReceiveEvent extends CancellableEvent {
+ /**
+ * The message that was received.
+ */
+ public final IChatComponent message;
+
+ public ChatReceiveEvent(IChatComponent message) {
+ this.message = message;
+ }
+
+ public String getFullyUnformattedMessage() {
+ return UTextComponent.Companion.stripFormatting(message.getUnformattedText());
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ReceivePacketEvent.java b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ReceivePacketEvent.java
new file mode 100644
index 0000000..17bc16d
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ReceivePacketEvent.java
@@ -0,0 +1,11 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import net.minecraft.network.Packet;
+
+public class ReceivePacketEvent extends CancellableEvent {
+ public final Packet<?> packet;
+
+ public ReceivePacketEvent(Packet<?> packet) {
+ this.packet = packet;
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ScreenOpenEvent.java b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ScreenOpenEvent.java
new file mode 100644
index 0000000..4593638
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/ScreenOpenEvent.java
@@ -0,0 +1,17 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import net.minecraft.client.gui.GuiScreen;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Called when a screen is opened or closed.
+ * If the screen is closed, {@link ScreenOpenEvent#screen} will be null.
+ */
+public class ScreenOpenEvent extends CancellableEvent {
+ @Nullable
+ public final GuiScreen screen;
+
+ public ScreenOpenEvent(@Nullable GuiScreen screen) {
+ this.screen = screen;
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/events/event/SendPacketEvent.java b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/SendPacketEvent.java
new file mode 100644
index 0000000..3cee784
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/SendPacketEvent.java
@@ -0,0 +1,11 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import net.minecraft.network.Packet;
+
+public class SendPacketEvent extends CancellableEvent {
+ public final Packet<?> packet;
+
+ public SendPacketEvent(Packet<?> packet) {
+ this.packet = packet;
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/events/event/TimerUpdateEvent.java b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/TimerUpdateEvent.java
new file mode 100644
index 0000000..a8b88dc
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/events/event/TimerUpdateEvent.java
@@ -0,0 +1,24 @@
+package cc.polyfrost.oneconfig.events.event;
+
+import net.minecraft.util.Timer;
+
+/**
+ * Called when the {@link Timer} is updated.
+ * Can be used as an alternative to getting instances of {@link Timer}
+ * via Mixin or Access Wideners / Transformers
+ */
+public class TimerUpdateEvent {
+ /**
+ * Whether the deltaTicks / renderPartialTicks was updated
+ */
+ public final boolean updatedDeltaTicks;
+ /**
+ * The {@link Timer} instance
+ */
+ public final Timer timer;
+
+ public TimerUpdateEvent(Timer timer, boolean updatedDeltaTicks) {
+ this.timer = timer;
+ this.updatedDeltaTicks = updatedDeltaTicks;
+ }
+} \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/OneConfig.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/OneConfig.java
new file mode 100644
index 0000000..7ecfe98
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/OneConfig.java
@@ -0,0 +1,93 @@
+package cc.polyfrost.oneconfig.internal;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.ShutdownEvent;
+import cc.polyfrost.oneconfig.internal.command.OneConfigCommand;
+import cc.polyfrost.oneconfig.internal.config.OneConfigConfig;
+import cc.polyfrost.oneconfig.internal.config.Preferences;
+import cc.polyfrost.oneconfig.internal.config.core.ConfigCore;
+import cc.polyfrost.oneconfig.internal.config.core.KeyBindHandler;
+import cc.polyfrost.oneconfig.internal.gui.BlurHandler;
+import cc.polyfrost.oneconfig.internal.hud.HudCore;
+import cc.polyfrost.oneconfig.libs.eventbus.Subscribe;
+import cc.polyfrost.oneconfig.utils.commands.CommandManager;
+import cc.polyfrost.oneconfig.utils.gui.GuiUtils;
+import cc.polyfrost.oneconfig.utils.hypixel.HypixelUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.util.ServiceLoader;
+
+/**
+ * The main class of OneConfig.
+ */
+@net.minecraftforge.fml.common.Mod(modid = "@ID@", name = "@NAME@", version = "@VER@")
+public class OneConfig {
+
+ public OneConfig() {
+ EventManager.INSTANCE.register(this);
+ }
+
+ public static final File oneConfigDir = new File("./OneConfig");
+ public static final Logger LOGGER = LogManager.getLogger("@NAME@");
+ private static boolean preLaunched = false;
+ private static boolean initialized = false;
+ private static boolean isObfuscated = true;
+
+ /**
+ * Called before mods are loaded.
+ * <p><b>SHOULD NOT BE CALLED!</b></p>
+ */
+ public static void preLaunch() {
+ if (preLaunched) return;
+ try {
+ Class.forName("net.minecraft.world.World");
+ LOGGER.warn("OneConfig is NOT obfuscated!");
+ isObfuscated = false;
+ } catch (Exception ignored) {
+ }
+ oneConfigDir.mkdirs();
+ new File(oneConfigDir, "profiles").mkdirs();
+ if (OneConfigConfig.getInstance() == null) {
+ OneConfigConfig.getInstance();
+ }
+ if (Preferences.getInstance() == null) {
+ Preferences.getInstance();
+ }
+ preLaunched = true;
+ }
+
+ /**
+ * Called after mods are loaded.
+ * <p><b>SHOULD NOT BE CALLED!</b></p>
+ */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ public static void init() {
+ if (initialized) return;
+ GuiUtils.getDeltaTime(); // called to make sure static initializer is called
+ try {
+ EventManager.INSTANCE.register(BlurHandler.INSTANCE);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ CommandManager.INSTANCE.registerCommand(OneConfigCommand.class);
+ EventManager.INSTANCE.register(new HudCore());
+ HypixelUtils.INSTANCE.initialize();
+ EventManager.INSTANCE.register(KeyBindHandler.INSTANCE);
+ ConfigCore.sortMods();
+
+ initialized = true;
+ }
+
+ /** Returns weather this is an obfuscated environment, using a check for obfuscated name of net.minecraft.world.World.class.
+ * @return true if this is an obfuscated environment, which is normal for Minecraft or false if not. */
+ public static boolean isObfuscated() {
+ return isObfuscated;
+ }
+
+ @Subscribe
+ private void onShutdown(ShutdownEvent event) {
+ ConfigCore.saveAll();
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/gui/impl/BlurHandlerImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/gui/impl/BlurHandlerImpl.java
new file mode 100644
index 0000000..0ef5f2b
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/gui/impl/BlurHandlerImpl.java
@@ -0,0 +1,132 @@
+package cc.polyfrost.oneconfig.internal.gui.impl;
+
+import cc.polyfrost.oneconfig.events.event.RenderEvent;
+import cc.polyfrost.oneconfig.events.event.ScreenOpenEvent;
+import cc.polyfrost.oneconfig.events.event.Stage;
+import cc.polyfrost.oneconfig.gui.OneConfigGui;
+import cc.polyfrost.oneconfig.internal.config.Preferences;
+import cc.polyfrost.oneconfig.internal.gui.BlurHandler;
+import cc.polyfrost.oneconfig.internal.mixin.ShaderGroupAccessor;
+import cc.polyfrost.oneconfig.libs.eventbus.Subscribe;
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.libs.universal.UScreen;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.shader.Shader;
+import net.minecraft.client.shader.ShaderUniform;
+import net.minecraft.util.ResourceLocation;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.List;
+
+/**
+ * An implementation of the BlurMC mod by tterrag1098.
+ * <p>
+ * For the original source see <a href="https://github.com/tterrag1098/Blur/blob/1.8.9/src/main/java/com/tterrag/blur/Blur.java">...</a>
+ * For the public license, see <a href="https://github.com/tterrag1098/Blur/blob/1.8.9/LICENSE">...</a>
+ * <p>
+ * License available under <a href="https://github.com/boomboompower/ToggleChat/blob/master/src/main/resources/licenses/BlurMC-License.txt">...</a>
+ *
+ * @author tterrag1098, boomboompower
+ * <p>
+ * Taken from ToggleChat
+ * <a href="https://github.com/boomboompower/ToggleChat/blob/master/LICENSE">...</a>
+ */
+public class BlurHandlerImpl implements BlurHandler {
+ private final ResourceLocation blurShader = new ResourceLocation("shaders/post/fade_in_blur.json");
+ private final Logger logger = LogManager.getLogger("OneConfig - Blur");
+ private long start;
+ private float progress = 0;
+
+ @Subscribe
+ private void onGuiChange(ScreenOpenEvent event) {
+ reloadBlur(event.screen);
+ }
+
+ @Subscribe
+ private void onRenderTick(RenderEvent event) {
+ if (event.stage != Stage.END) {
+ return;
+ }
+
+ // Only blur on our own menus
+ if (UScreen.getCurrentScreen() == null) {
+ return;
+ }
+
+ // Only update the shader if one is active
+ if (!UMinecraft.getMinecraft().entityRenderer.isShaderActive()) {
+ return;
+ }
+ if (progress >= 5) return;
+ progress = getBlurStrengthProgress();
+
+ // This is hilariously bad, and could cause frame issues on low-end computers.
+ // Why is this being computed every tick? Surely there is a better way?
+ // This needs to be optimized.
+ try {
+ final List<Shader> listShaders = ((ShaderGroupAccessor) Minecraft.getMinecraft().entityRenderer.getShaderGroup()).getListShaders();
+
+ // Should not happen. Something bad happened.
+ if (listShaders == null) {
+ return;
+ }
+
+ // Iterate through the list of shaders.
+ for (Shader shader : listShaders) {
+ ShaderUniform su = shader.getShaderManager().getShaderUniform("Progress");
+
+ if (su == null) {
+ continue;
+ }
+
+ // All this for this.
+ su.set(progress);
+ }
+ } catch (IllegalArgumentException ex) {
+ this.logger.error("An error.png occurred while updating OneConfig's blur. Please report this!", ex);
+ }
+ }
+
+ /**
+ * Activates/deactivates the blur in the current world if
+ * 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(Object gui) {
+ // Don't do anything if no world is loaded
+ if (UMinecraft.getWorld() == null) {
+ return;
+ }
+
+ // If a shader is not already active and the UI is
+ // a one of ours, we should load our own blur!
+
+ if (!UMinecraft.getMinecraft().entityRenderer.isShaderActive() && gui instanceof OneConfigGui && Preferences.enableBlur) {
+ UMinecraft.getMinecraft().entityRenderer.loadShader(this.blurShader);
+
+ this.start = System.currentTimeMillis();
+ this.progress = 0;
+
+ // If a shader is active and the incoming UI is null or we have blur disabled, stop using the shader.
+ } else if (UMinecraft.getMinecraft().entityRenderer.isShaderActive() && (gui == null || !Preferences.enableBlur)) {
+ String name = UMinecraft.getMinecraft().entityRenderer.getShaderGroup().getShaderGroupName();
+
+ // Only stop our specific blur ;)
+ if (!name.endsWith("fade_in_blur.json")) {
+ return;
+ }
+
+ UMinecraft.getMinecraft().entityRenderer.stopUseShader();
+ }
+ }
+
+ /**
+ * Returns the strength of the blur as determined by the duration the effect of the blur.
+ * <p>
+ * The strength of the blur does not go below 5.0F.
+ */
+ private float getBlurStrengthProgress() {
+ return Math.min((System.currentTimeMillis() - this.start) / 50F, 5.0F);
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/GuiIngameForgeMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/GuiIngameForgeMixin.java
new file mode 100644
index 0000000..2a89972
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/GuiIngameForgeMixin.java
@@ -0,0 +1,19 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.HudRenderEvent;
+import net.minecraftforge.client.GuiIngameForge;
+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(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, remap = false), remap = true)
+ private void onRenderGameOverlay(float partialTicks, CallbackInfo ci) {
+ EventManager.INSTANCE.post(new HudRenderEvent(partialTicks));
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/MinecraftMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/MinecraftMixin.java
new file mode 100644
index 0000000..0603d12
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/MinecraftMixin.java
@@ -0,0 +1,100 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import cc.polyfrost.oneconfig.internal.OneConfig;
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.*;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.Timer;
+import net.minecraftforge.client.event.GuiOpenEvent;
+import net.minecraftforge.fml.common.eventhandler.Event;
+import org.objectweb.asm.Opcodes;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Minecraft.class)
+public class MinecraftMixin {
+ @Shadow
+ private Timer timer;
+
+ @Inject(method = "shutdownMinecraftApplet", at = @At("HEAD"))
+ private void onShutdown(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new PreShutdownEvent());
+ }
+
+ @Inject(method = "startGame", at = @At("HEAD"))
+ private void onStart(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new StartEvent());
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> EventManager.INSTANCE.post(new ShutdownEvent())));
+ }
+
+ @Inject(method = "startGame", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/FMLClientHandler;beginMinecraftLoading(Lnet/minecraft/client/Minecraft;Ljava/util/List;Lnet/minecraft/client/resources/IReloadableResourceManager;)V", remap = false), remap = true)
+ private void onPreLaunch(CallbackInfo ci) {
+ OneConfig.preLaunch();
+ }
+
+ @Inject(method = "startGame", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/FMLClientHandler;onInitializationComplete()V", shift = At.Shift.AFTER, remap = false), remap = true)
+ private void onInit(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new InitializationEvent());
+ OneConfig.init();
+ }
+
+ @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.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, remap = false), remap = true)
+ private void onRenderTickEnd(CallbackInfo ci) {
+ 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, remap = false), remap = true)
+ private void onClientTickStart(CallbackInfo ci) {
+ 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, remap = false), remap = true)
+ private void onClientTickEnd(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new TickEvent(Stage.END));
+ }
+
+ @ModifyArg(method = "displayGuiScreen", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/eventhandler/EventBus;post(Lnet/minecraftforge/fml/common/eventhandler/Event;)Z", remap = false), remap = true)
+ private Event onGuiOpenEvent(Event a) {
+ if (a instanceof GuiOpenEvent) {
+ GuiOpenEvent forgeEvent = (GuiOpenEvent) a;
+ ScreenOpenEvent event = new ScreenOpenEvent(forgeEvent.gui);
+ EventManager.INSTANCE.post(event);
+ if (event.isCancelled) {
+ forgeEvent.setCanceled(true);
+ }
+ return forgeEvent;
+ }
+ return a;
+ }
+
+ @Inject(method = "runGameLoop", at = @At(value = "FIELD", target = "Lnet/minecraft/util/Timer;renderPartialTicks:F", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER))
+ private void onNonDeltaTickTimerUpdate(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new TimerUpdateEvent(timer, false));
+ }
+
+ @Inject(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Timer;updateTimer()V", shift = At.Shift.AFTER, ordinal = 1))
+ private void onDeltaTickTimerUpdate(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new TimerUpdateEvent(timer, true));
+ }
+
+ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;fireKeyInput()V"))
+ private void onKeyEvent(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new KeyInputEvent());
+ }
+
+ @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/common/FMLCommonHandler;fireMouseInput()V"))
+ private void onMouseEvent(CallbackInfo ci) {
+ EventManager.INSTANCE.post(new MouseInputEvent());
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetHandlerPlayClientMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetHandlerPlayClientMixin.java
new file mode 100644
index 0000000..7a446c7
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetHandlerPlayClientMixin.java
@@ -0,0 +1,38 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.ChatReceiveEvent;
+import cc.polyfrost.oneconfig.events.event.SendPacketEvent;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.network.Packet;
+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(value = NetHandlerPlayClient.class, priority = Integer.MAX_VALUE)
+public class NetHandlerPlayClientMixin {
+
+ @Inject(method = "addToSendQueue", at = @At("HEAD"), cancellable = true)
+ private void onSendPacket(Packet<?> p_147297_1_, CallbackInfo ci) {
+ SendPacketEvent event = new SendPacketEvent(p_147297_1_);
+ EventManager.INSTANCE.post(event);
+ if (event.isCancelled) {
+ ci.cancel();
+ }
+ }
+
+ @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();
+ }
+ }
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetworkManagerMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetworkManagerMixin.java
new file mode 100644
index 0000000..3da0c91
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/NetworkManagerMixin.java
@@ -0,0 +1,25 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.ReceivePacketEvent;
+import io.netty.channel.ChannelHandlerContext;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.network.Packet;
+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(value = NetworkManager.class, priority = Integer.MAX_VALUE)
+public class NetworkManagerMixin {
+ @Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/Packet;)V", at = @At("HEAD"), cancellable = true)
+ private void onReceivePacket(ChannelHandlerContext p_channelRead0_1_, Packet<?> p_channelRead0_2_, CallbackInfo ci) {
+ ReceivePacketEvent event = new ReceivePacketEvent(p_channelRead0_2_);
+ EventManager.INSTANCE.post(event);
+ if (event.isCancelled) {
+ ci.cancel();
+ }
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/OptifineConfigMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/OptifineConfigMixin.java
new file mode 100644
index 0000000..9012091
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/OptifineConfigMixin.java
@@ -0,0 +1,23 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import cc.polyfrost.oneconfig.internal.plugin.hooks.OptifineConfigHook;
+import org.spongepowered.asm.mixin.Dynamic;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Pseudo;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Pseudo
+@Mixin(targets = "Config", remap = false)
+public class OptifineConfigMixin {
+ @Dynamic("OptiFine")
+ @Inject(method = "isFastRender", at = @At("HEAD"), cancellable = true)
+ private static void cancelFastRender(CallbackInfoReturnable<Boolean> cir) {
+ if (OptifineConfigHook.shouldNotApplyFastRender()) {
+ cir.setReturnValue(false);
+ }
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/ShaderGroupAccessor.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/ShaderGroupAccessor.java
new file mode 100644
index 0000000..588da19
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/ShaderGroupAccessor.java
@@ -0,0 +1,16 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import net.minecraft.client.shader.Shader;
+import net.minecraft.client.shader.ShaderGroup;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.util.List;
+
+@Mixin(ShaderGroup.class)
+public interface ShaderGroupAccessor {
+ @Accessor("listShaders")
+ List<Shader> getListShaders();
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/VigilantMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/VigilantMixin.java
new file mode 100644
index 0000000..6b474ab
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/VigilantMixin.java
@@ -0,0 +1,10 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.mixin;
+
+import gg.essential.vigilance.Vigilant;
+import org.spongepowered.asm.mixin.Mixin;
+
+@Mixin(Vigilant.class)
+public class VigilantMixin {
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/WorldClientMixin.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/WorldClientMixin.java
new file mode 100644
index 0000000..05c6920
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/mixin/WorldClientMixin.java
@@ -0,0 +1,23 @@
+//#if MC==10809
+package cc.polyfrost.oneconfig.internal.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());
+ }
+}
+//#endif \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java
new file mode 100644
index 0000000..52256ca
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ClassTransformer.java
@@ -0,0 +1,102 @@
+package cc.polyfrost.oneconfig.internal.plugin.asm;
+
+import cc.polyfrost.oneconfig.internal.plugin.asm.tweakers.NanoVGGLConfigTransformer;
+import cc.polyfrost.oneconfig.internal.plugin.asm.tweakers.VigilantTransformer;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import net.minecraft.launchwrapper.IClassTransformer;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.tree.ClassNode;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Taken from LWJGLTwoPointFive under The Unlicense
+ * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a>
+ * <p>also half taken from asmworkspace by asbyth ty</p>
+ */
+@SuppressWarnings("unused")
+public class ClassTransformer implements IClassTransformer {
+ private static final Logger logger = LogManager.getLogger("OneConfig ASM");
+ private final Multimap<String, ITransformer> transformerMap = ArrayListMultimap.create();
+ private static final boolean outputBytecode = Boolean.parseBoolean(System.getProperty("debugBytecode", "false"));
+
+ public ClassTransformer() {
+ registerTransformer(new NanoVGGLConfigTransformer());
+ registerTransformer(new VigilantTransformer());
+ }
+
+ private void registerTransformer(ITransformer transformer) {
+ // loop through names of classes
+ for (String cls : transformer.getClassName()) {
+ // put the classes into the transformer map
+ transformerMap.put(cls, transformer);
+ }
+ }
+
+ @Override
+ public byte[] transform(String name, String transformedName, byte[] bytes) {
+ if (bytes == null) return null;
+
+ Collection<ITransformer> transformers = transformerMap.get(transformedName);
+ if (transformers.isEmpty()) return bytes;
+
+ ClassReader reader = new ClassReader(bytes);
+ ClassNode node = new ClassNode();
+ reader.accept(node, ClassReader.EXPAND_FRAMES);
+
+ for (ITransformer transformer : transformers) {
+ transformer.transform(transformedName, node);
+ }
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ try {
+ node.accept(cw);
+ } catch (Throwable t) {
+ logger.error("Exception when transforming " + transformedName + " : " + t.getClass().getSimpleName());
+ t.printStackTrace();
+ }
+
+ if (outputBytecode) {
+ File bytecodeDirectory = new File("bytecode");
+ String transformedClassName;
+
+ // anonymous classes
+ if (transformedName.contains("$")) {
+ transformedClassName = transformedName.replace('$', '.') + ".class";
+ } else {
+ transformedClassName = transformedName + ".class";
+ }
+
+ if (!bytecodeDirectory.exists()) {
+ bytecodeDirectory.mkdirs();
+ }
+
+ File bytecodeOutput = new File(bytecodeDirectory, transformedClassName);
+
+ try {
+ if (!bytecodeOutput.exists()) {
+ bytecodeOutput.createNewFile();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try (FileOutputStream os = new FileOutputStream(bytecodeOutput)) {
+ // write to the generated class to /run/bytecode/classfile.class
+ // with the class bytes from transforming
+ os.write(cw.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return cw.toByteArray();
+ }
+} \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java
new file mode 100644
index 0000000..1bc50d1
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/ITransformer.java
@@ -0,0 +1,24 @@
+package cc.polyfrost.oneconfig.internal.plugin.asm;
+
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public interface ITransformer {
+ String[] getClassName();
+
+ void transform(String transformedName, ClassNode node);
+
+ default void clearInstructions(MethodNode methodNode) {
+ methodNode.instructions.clear();
+
+ // dont waste time clearing local variables if they're empty
+ if (!methodNode.localVariables.isEmpty()) {
+ methodNode.localVariables.clear();
+ }
+
+ // dont waste time clearing try-catches if they're empty
+ if (!methodNode.tryCatchBlocks.isEmpty()) {
+ methodNode.tryCatchBlocks.clear();
+ }
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java
new file mode 100644
index 0000000..d302b9d
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/OneConfigTweaker.java
@@ -0,0 +1,117 @@
+package cc.polyfrost.oneconfig.internal.plugin.asm;
+
+import cc.polyfrost.oneconfig.internal.init.OneConfigInit;
+import net.minecraft.launchwrapper.ITweaker;
+import net.minecraft.launchwrapper.Launch;
+import net.minecraft.launchwrapper.LaunchClassLoader;
+import net.minecraftforge.fml.relauncher.CoreModManager;
+import org.spongepowered.asm.launch.MixinBootstrap;
+import org.spongepowered.asm.launch.MixinTweaker;
+import org.spongepowered.asm.mixin.Mixins;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+
+public class OneConfigTweaker implements ITweaker {
+
+ public OneConfigTweaker() {
+ for (URL url : Launch.classLoader.getSources()) {
+ doMagicMixinStuff(url);
+ }
+ }
+
+ private void doMagicMixinStuff(URL url) {
+ try {
+ URI uri = url.toURI();
+ if (Objects.equals(uri.getScheme(), "file")) {
+ File file = new File(uri);
+ if (file.exists() && file.isFile()) {
+ try (JarFile jarFile = new JarFile(file)) {
+ if (jarFile.getManifest() != null) {
+ Attributes attributes = jarFile.getManifest().getMainAttributes();
+ String tweakerClass = attributes.getValue("TweakClass");
+ if (Objects.equals(tweakerClass, "cc.polyfrost.oneconfigwrapper.OneConfigWrapper")) {
+ CoreModManager.getIgnoredMods().remove(file.getName());
+ CoreModManager.getReparseableCoremods().add(file.getName());
+ String mixinConfig = attributes.getValue("MixinConfigs");
+ if (mixinConfig != null) {
+ try {
+ try {
+ List<String> tweakClasses = (List<String>) Launch.blackboard.get("TweakClasses"); // tweak classes before other mod trolling
+ if (tweakClasses.contains("org.spongepowered.asm.launch.MixinTweaker")) { // if there's already a mixin tweaker, we'll just load it like "usual"
+ new MixinTweaker(); // also we might not need to make a new mixin tweawker all the time but im just making sure
+ } else if (!Launch.blackboard.containsKey("mixin.initialised")) { // if there isnt, we do our own trolling
+ List<ITweaker> tweaks = (List<ITweaker>) Launch.blackboard.get("Tweaks");
+ tweaks.add(new MixinTweaker());
+ }
+ } catch (Exception ignored) {
+ // if it fails i *think* we can just ignore it
+ }
+ MixinBootstrap.getPlatform().addContainer(uri);
+ } catch (Exception ignored) {
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception ignored) {
+
+ }
+ }
+
+ @Override
+ public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
+ MixinBootstrap.init();
+ boolean captureNext = false;
+ for (String arg : args) {
+ if (captureNext) {
+ Mixins.addConfiguration(arg);
+ }
+ captureNext = "--mixin".equals(arg);
+ }
+ }
+
+ @Override
+ public void injectIntoClassLoader(LaunchClassLoader classLoader) {
+ removeLWJGLException();
+ Launch.classLoader.registerTransformer(ClassTransformer.class.getName());
+ OneConfigInit.initialize(new String[]{});
+ Launch.blackboard.put("oneconfig.init.initialized", true);
+ Launch.classLoader.addClassLoaderExclusion("cc.polyfrost.oneconfig.internal.plugin.asm.");
+ }
+
+ /**
+ * Taken from LWJGLTwoPointFive under The Unlicense
+ * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a>
+ */
+ private void removeLWJGLException() {
+ try {
+ Field f_exceptions = LaunchClassLoader.class.getDeclaredField("classLoaderExceptions");
+ f_exceptions.setAccessible(true);
+ Set<String> exceptions = (Set<String>) f_exceptions.get(Launch.classLoader);
+ exceptions.remove("org.lwjgl.");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getLaunchTarget() {
+ return null;
+ }
+
+ @Override
+ public String[] getLaunchArguments() {
+ return new String[0];
+ }
+} \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java
new file mode 100644
index 0000000..bbbf4a1
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/NanoVGGLConfigTransformer.java
@@ -0,0 +1,47 @@
+package cc.polyfrost.oneconfig.internal.plugin.asm.tweakers;
+
+import cc.polyfrost.oneconfig.internal.plugin.asm.ITransformer;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.*;
+
+/**
+ * Taken from LWJGLTwoPointFive under The Unlicense
+ * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a>
+ */
+public class NanoVGGLConfigTransformer implements ITransformer {
+ @Override
+ public String[] getClassName() {
+ return new String[]{"org.lwjgl.nanovg.NanoVGGLConfig"};
+ }
+
+ @Override
+ public void transform(String transformedName, ClassNode node) {
+ for (MethodNode method : node.methods) {
+ if (method.name.equals("configGL")) {
+ InsnList list = new InsnList();
+
+ list.add(new VarInsnNode(Opcodes.LLOAD, 0));
+ list.add(new TypeInsnNode(Opcodes.NEW, "cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider"));
+ list.add(new InsnNode(Opcodes.DUP));
+ list.add(new MethodInsnNode(
+ Opcodes.INVOKESPECIAL,
+ "cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider",
+ "<init>",
+ "()V",
+ false
+ ));
+ list.add(new MethodInsnNode(
+ Opcodes.INVOKESTATIC,
+ "org/lwjgl/nanovg/NanoVGGLConfig",
+ "config",
+ "(JLorg/lwjgl/system/FunctionProvider;)V",
+ false
+ ));
+ list.add(new InsnNode(Opcodes.RETURN));
+
+ clearInstructions(method);
+ method.instructions.insert(list);
+ }
+ }
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java
new file mode 100644
index 0000000..8dd60cf
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/asm/tweakers/VigilantTransformer.java
@@ -0,0 +1,89 @@
+package cc.polyfrost.oneconfig.internal.plugin.asm.tweakers;
+
+import cc.polyfrost.oneconfig.internal.plugin.asm.ITransformer;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.*;
+
+import java.io.File;
+
+public class VigilantTransformer implements ITransformer {
+
+ @Override
+ public String[] getClassName() {
+ return new String[]{"gg.essential.vigilance.Vigilant"};
+ }
+
+ /**
+ * If anything here is changed, edit the corresponding method in OneConfigMixinPlugin!
+ */
+ @Override
+ public void transform(String transformedName, ClassNode node) {
+ if (!node.interfaces.contains("cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilantAccessor")) {
+ node.fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;", null, null));
+ node.fields.add(new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "oneconfig$file", Type.getDescriptor(File.class), null, null));
+
+ node.interfaces.add("cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilantAccessor");
+ MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC, "getPropertyCollector", "()Lgg/essential/vigilance/data/PropertyCollector;", null, null);
+ LabelNode labelNode = new LabelNode();
+ methodNode.instructions.add(labelNode);
+ methodNode.instructions.add(new LineNumberNode(421421, labelNode));
+ methodNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ methodNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "propertyCollector", "Lgg/essential/vigilance/data/PropertyCollector;"));
+ methodNode.instructions.add(new InsnNode(Opcodes.ARETURN));
+ node.methods.add(methodNode);
+
+ MethodNode methodNode2 = new MethodNode(Opcodes.ACC_PUBLIC, "handleOneConfigDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", null, null);
+ LabelNode labelNode2 = new LabelNode();
+ LabelNode labelNode3 = new LabelNode();
+ LabelNode labelNode4 = new LabelNode();
+ methodNode2.instructions.add(labelNode2);
+ methodNode2.instructions.add(new LineNumberNode(15636436, labelNode2));
+ methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ methodNode2.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;"));
+
+ methodNode2.instructions.add(new JumpInsnNode(Opcodes.IFNULL, labelNode4));
+
+ methodNode2.instructions.add(labelNode3);
+ methodNode2.instructions.add(new LineNumberNode(15636437, labelNode3));
+ methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ methodNode2.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;"));
+ methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ methodNode2.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2));
+ methodNode2.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "cc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig", "addDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", false));
+
+ methodNode2.instructions.add(labelNode4);
+ methodNode2.instructions.add(new LineNumberNode(15636438, labelNode4));
+ methodNode2.instructions.add(new InsnNode(Opcodes.RETURN));
+ node.methods.add(methodNode2);
+
+ for (MethodNode method : node.methods) {
+ if (method.name.equals("initialize")) {
+ InsnList list = new InsnList();
+ list.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ list.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ list.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ list.add(new FieldInsnNode(Opcodes.GETFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$file", Type.getDescriptor(File.class)));
+ list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook", "returnNewConfig", "(Lgg/essential/vigilance/Vigilant;Ljava/io/File;)Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;", false));
+ list.add(new FieldInsnNode(Opcodes.PUTFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$config", "Lcc/polyfrost/oneconfig/config/compatibility/vigilance/VigilanceConfig;"));
+ method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list);
+ } else if (method.name.equals("addDependency") && method.desc.equals("(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V")) {
+ InsnList list = new InsnList();
+
+ list.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ list.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ list.add(new VarInsnNode(Opcodes.ALOAD, 2));
+ list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "gg/essential/vigilance/Vigilant", "handleOneConfigDependency", "(Lgg/essential/vigilance/data/PropertyData;Lgg/essential/vigilance/data/PropertyData;)V", false));
+
+ method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list);
+ } else if (method.name.equals("<init>") && method.desc.equals("(Ljava/io/File;Ljava/lang/String;Lgg/essential/vigilance/data/PropertyCollector;Lgg/essential/vigilance/data/SortingBehavior;)V")) {
+ InsnList list = new InsnList();
+ list.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ list.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ list.add(new FieldInsnNode(Opcodes.PUTFIELD, "gg/essential/vigilance/Vigilant", "oneconfig$file", Type.getDescriptor(File.class)));
+ method.instructions.insertBefore(method.instructions.getLast().getPrevious(), list);
+ }
+ }
+ }
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java
new file mode 100644
index 0000000..297c84d
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/Lwjgl2FunctionProvider.java
@@ -0,0 +1,47 @@
+package cc.polyfrost.oneconfig.internal.plugin.hooks;
+
+import org.lwjgl.system.FunctionProvider;
+
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+/**
+ * Taken from LWJGLTwoPointFive under The Unlicense
+ * <a href="https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/">https://github.com/DJtheRedstoner/LWJGLTwoPointFive/blob/master/LICENSE/</a>
+ */
+public class Lwjgl2FunctionProvider implements FunctionProvider {
+
+ private static final Class<?> GLContext;
+ private final Method m_getFunctionAddress;
+
+ static {
+ try {
+ GLContext = Class.forName("org.lwjgl.opengl.GLContext");
+ } catch (Throwable t) {
+ throw new RuntimeException("Unable to initialize LWJGL2FunctionProvider", t);
+ }
+ }
+
+ public Lwjgl2FunctionProvider() {
+ try {
+ m_getFunctionAddress = GLContext.getDeclaredMethod("getFunctionAddress", String.class);
+ m_getFunctionAddress.setAccessible(true);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public long getFunctionAddress(CharSequence functionName) {
+ try {
+ return (long) m_getFunctionAddress.invoke(null, functionName.toString());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public long getFunctionAddress(ByteBuffer byteBuffer) {
+ throw new UnsupportedOperationException();
+ }
+} \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java
new file mode 100644
index 0000000..dea68cf
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/OptifineConfigHook.java
@@ -0,0 +1,22 @@
+package cc.polyfrost.oneconfig.internal.plugin.hooks;
+
+import cc.polyfrost.oneconfig.gui.OneConfigGui;
+import cc.polyfrost.oneconfig.platform.Platform;
+import cc.polyfrost.oneconfig.utils.gui.GuiUtils;
+
+import java.util.Optional;
+
+public class OptifineConfigHook {
+
+ public static boolean shouldNotApplyFastRender() {
+ if (Platform.getGuiPlatform().getCurrentScreen() instanceof OneConfigGui) {
+ return true;
+ }
+ for (Optional screen : GuiUtils.getScreenQueue()) {
+ if (screen.isPresent() && screen.get() instanceof OneConfigGui) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java
new file mode 100644
index 0000000..a012773
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/internal/plugin/hooks/VigilantHook.java
@@ -0,0 +1,26 @@
+package cc.polyfrost.oneconfig.internal.plugin.hooks;
+
+import cc.polyfrost.oneconfig.config.compatibility.vigilance.VigilanceConfig;
+import cc.polyfrost.oneconfig.config.data.Mod;
+import cc.polyfrost.oneconfig.config.data.ModType;
+import cc.polyfrost.oneconfig.internal.config.core.ConfigCore;
+import cc.polyfrost.oneconfig.platform.Platform;
+import gg.essential.vigilance.Vigilant;
+
+import java.io.File;
+
+@SuppressWarnings("unused")
+public class VigilantHook {
+ public static VigilanceConfig returnNewConfig(Vigilant vigilant, File file) {
+ if (vigilant != null && Platform.getInstance().isCallingFromMinecraftThread()) {
+ String name = !vigilant.getGuiTitle().equals("Settings") ? vigilant.getGuiTitle() : !Platform.getLoaderPlatform().hasActiveModContainer() ? "Unknown" : Platform.getLoaderPlatform().getActiveModContainer().name;
+ if (name.equals("OneConfig")) name = "Essential";
+ String finalName = name;
+ // duplicate fix
+ if (ConfigCore.mods.stream().anyMatch(mod -> mod.name.equals(finalName))) return null;
+ return new VigilanceConfig(new Mod(name, ModType.THIRD_PARTY), file.getAbsolutePath(), vigilant);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GLPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GLPlatformImpl.java
new file mode 100644
index 0000000..3ee962b
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GLPlatformImpl.java
@@ -0,0 +1,67 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.libs.universal.UMatrixStack;
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.platform.GLPlatform;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.client.shader.Framebuffer;
+
+@SuppressWarnings("unused")
+public class GLPlatformImpl implements GLPlatform {
+
+ @Override
+ public void drawRect(float x, float y, float x2, float y2, int color) {
+ if (x < x2) {
+ float i = x;
+ x = x2;
+ x2 = i;
+ }
+
+ if (y < y2) {
+ float i = y;
+ y = y2;
+ y2 = i;
+ }
+
+ float f = (float)(color >> 24 & 0xFF) / 255.0F;
+ float g = (float)(color >> 16 & 0xFF) / 255.0F;
+ float h = (float)(color >> 8 & 0xFF) / 255.0F;
+ float j = (float)(color & 0xFF) / 255.0F;
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldRenderer = tessellator.getWorldRenderer();
+ GlStateManager.enableBlend();
+ GlStateManager.disableTexture2D();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.color(g, h, j, f);
+ worldRenderer.begin(7, DefaultVertexFormats.POSITION);
+ worldRenderer.pos(x, y2, 0.0).endVertex();
+ worldRenderer.pos(x2, y2, 0.0).endVertex();
+ worldRenderer.pos(x2, y, 0.0).endVertex();
+ worldRenderer.pos(x, y, 0.0).endVertex();
+ tessellator.draw();
+ GlStateManager.enableTexture2D();
+ GlStateManager.disableBlend();
+ }
+
+ @Override
+ public void enableStencil() {
+ Framebuffer framebuffer = Minecraft.getMinecraft().getFramebuffer();
+ if (!framebuffer.isStencilEnabled()) {
+ framebuffer.enableStencil();
+ }
+ }
+
+ @Override
+ public float drawText(UMatrixStack matrixStack, String text, float x, float y, int color, boolean shadow) {
+ return UMinecraft.getFontRenderer().drawString(text, x, y, color, shadow);
+ }
+
+ @Override
+ public int getStringWidth(String text) {
+ return UMinecraft.getFontRenderer().getStringWidth(text);
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GuiPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GuiPlatformImpl.java
new file mode 100644
index 0000000..6ea3c03
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/GuiPlatformImpl.java
@@ -0,0 +1,31 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.libs.universal.UScreen;
+import cc.polyfrost.oneconfig.platform.GuiPlatform;
+import net.minecraft.client.gui.GuiChat;
+import net.minecraft.client.gui.GuiScreen;
+
+@SuppressWarnings("unused")
+public class GuiPlatformImpl implements GuiPlatform {
+
+ @Override
+ public Object getCurrentScreen() {
+ return UScreen.getCurrentScreen();
+ }
+
+ @Override
+ public void setCurrentScreen(Object screen) {
+ UScreen.displayScreen((GuiScreen) screen);
+ }
+
+ @Override
+ public boolean isInChat() {
+ return getCurrentScreen() instanceof GuiChat;
+ }
+
+ @Override
+ public boolean isInDebug() {
+ return UMinecraft.getSettings().showDebugInfo;
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl.java
new file mode 100644
index 0000000..1913238
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl.java
@@ -0,0 +1,13 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.platform.I18nPlatform;
+import net.minecraft.client.resources.I18n;
+
+@SuppressWarnings("unused")
+public class I18nPlatformImpl implements I18nPlatform {
+
+ @Override
+ public String format(String key, Object... args) {
+ return I18n.format(key, args);
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java
new file mode 100644
index 0000000..01269cd
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java
@@ -0,0 +1,26 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.platform.LoaderPlatform;
+import net.minecraftforge.fml.common.Loader;
+import net.minecraftforge.fml.common.ModContainer;
+
+@SuppressWarnings("unused")
+public class LoaderPlatformImpl implements LoaderPlatform {
+ @Override
+ public boolean isModLoaded(String id) {
+ return Loader.isModLoaded(id);
+ }
+
+ @Override
+ public boolean hasActiveModContainer() {
+ return Loader.instance().activeModContainer() != null;
+ }
+
+ @Override
+ public ActiveMod getActiveModContainer() {
+ ModContainer container = Loader.instance().activeModContainer();
+ if (container == null)
+ return null;
+ return new ActiveMod(container.getName(), container.getModId(), container.getVersion());
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl.java
new file mode 100644
index 0000000..1276000
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl.java
@@ -0,0 +1,53 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.platform.MousePlatform;
+import org.lwjgl.input.Mouse;
+
+@SuppressWarnings("unused")
+public class MousePlatformImpl implements MousePlatform {
+
+ @Override
+ public int getMouseX() {
+ return Mouse.getX();
+ }
+
+ @Override
+ public int getMouseY() {
+ return Mouse.getY();
+ }
+
+ @Override
+ public int getDWheel() {
+ return Mouse.getDWheel();
+ }
+
+ @Override
+ public int getMouseDX() {
+ return Mouse.getDX();
+ }
+
+ @Override
+ public int getMouseDY() {
+ return Mouse.getDY();
+ }
+
+ @Override
+ public boolean next() {
+ return Mouse.next();
+ }
+
+ @Override
+ public boolean getEventButtonState() {
+ return Mouse.getEventButtonState();
+ }
+
+ @Override
+ public int getEventButton() {
+ return Mouse.getEventButton();
+ }
+
+ @Override
+ public boolean isButtonDown(int button) {
+ return Mouse.isButtonDown(button);
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java
new file mode 100644
index 0000000..7ad639c
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java
@@ -0,0 +1,12 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.platform.Platform;
+import net.minecraft.client.Minecraft;
+
+@SuppressWarnings("unused")
+public class PlatformImpl implements Platform {
+ @Override
+ public boolean isCallingFromMinecraftThread() {
+ return Minecraft.getMinecraft().isCallingFromMinecraftThread();
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl.java
new file mode 100644
index 0000000..81454b0
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl.java
@@ -0,0 +1,22 @@
+package cc.polyfrost.oneconfig.platform.impl;
+
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.platform.ServerPlatform;
+import net.minecraft.client.entity.EntityPlayerSP;
+
+@SuppressWarnings("unused")
+public class ServerPlatformImpl implements ServerPlatform {
+
+ @Override
+ public boolean inMultiplayer() {
+ return UMinecraft.getWorld() != null && !UMinecraft.getMinecraft().isSingleplayer();
+ }
+
+ @Override
+ public String getServerBrand() {
+ EntityPlayerSP player = UMinecraft.getPlayer();
+ if (player == null)
+ return null;
+ return player.getClientBrand();
+ }
+}
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/utils/commands/PlatformCommandManagerImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/utils/commands/PlatformCommandManagerImpl.java
new file mode 100644
index 0000000..8b6edcc
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/utils/commands/PlatformCommandManagerImpl.java
@@ -0,0 +1,330 @@
+package cc.polyfrost.oneconfig.utils.commands;
+
+import cc.polyfrost.oneconfig.libs.universal.ChatColor;
+import cc.polyfrost.oneconfig.libs.universal.UChat;
+import cc.polyfrost.oneconfig.utils.commands.annotations.Command;
+import cc.polyfrost.oneconfig.utils.commands.annotations.Greedy;
+import cc.polyfrost.oneconfig.utils.commands.annotations.Name;
+import cc.polyfrost.oneconfig.utils.commands.annotations.SubCommand;
+import cc.polyfrost.oneconfig.utils.commands.arguments.ArgumentParser;
+import cc.polyfrost.oneconfig.utils.commands.arguments.Arguments;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.BlockPos;
+import net.minecraftforge.client.ClientCommandHandler;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Parameter;
+import java.util.*;
+
+import static cc.polyfrost.oneconfig.utils.commands.CommandManager.*;
+
+public class PlatformCommandManagerImpl implements PlatformCommandManager {
+
+ @Override
+ public void createCommand(CommandManager.InternalCommand root, Command annotation) {
+ ClientCommandHandler.instance.registerCommand(new CommandBase() {
+ @Override
+ public String getCommandName() {
+ return annotation.value();
+ }
+
+ @Override
+ public String getCommandUsage(ICommandSender sender) {
+ return "/" + annotation.value();
+ }
+
+ @Override
+ public void processCommand(ICommandSender sender, String[] args) {
+ handleCommand(root, annotation, args);
+ }
+
+ @Override
+ public int getRequiredPermissionLevel() {
+ return -1;
+ }
+
+ @Override
+ public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) {
+ return handleTabCompletion(root, annotation, args);
+ }
+ });
+ }
+
+ private void handleCommand(CommandManager.InternalCommand root, Command annotation, String[] args) {
+ if (args.length == 0) {
+ if (!root.invokers.isEmpty()) {
+ try {
+ root.invokers.get(0).method.invoke(null);
+ } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException |
+ ExceptionInInitializerError e) {
+ e.printStackTrace();
+ UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR);
+ }
+ }
+ } else {
+ if (annotation.helpCommand() && args[0].equalsIgnoreCase("help")) {
+ UChat.chat(sendHelpCommand(root));
+ } else {
+ List<CommandManager.InternalCommand.InternalCommandInvoker> commands = new ArrayList<>();
+ int depth = 0;
+ for (CommandManager.InternalCommand command : root.children) {
+ int newDepth = loopThroughCommands(commands, 0, command, args);
+ if (newDepth != -1) {
+ depth = newDepth;
+ break;
+ }
+ }
+ if (commands.isEmpty()) {
+ if (depth == -2) {
+ UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + TOO_MANY_PARAMETERS.replace("@ROOT_COMMAND@", annotation.value()));
+ } else {
+ UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + NOT_FOUND_TEXT.replace("@ROOT_COMMAND@", annotation.value()));
+ }
+ } else {
+ List<CommandManager.CustomError> errors = new ArrayList<>();
+ for (CommandManager.InternalCommand.InternalCommandInvoker invoker : commands) {
+ try {
+ List<Object> params = getParametersForInvoker(invoker, depth, args);
+ if (params.size() == 1) {
+ Object first = params.get(0);
+ if (first instanceof CommandManager.CustomError) {
+ errors.add((CommandManager.CustomError) first);
+ continue;
+ }
+ }
+ invoker.method.invoke(null, params.toArray());
+ return;
+ } catch (Exception e) {
+ e.printStackTrace();
+ UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR);
+ return;
+ }
+ }
+ //noinspection ConstantConditions
+ if (!errors.isEmpty()) {
+ UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + "Multiple errors occurred:");
+ for (CommandManager.CustomError error : errors) {
+ UChat.chat(" " + ChatColor.RED + ChatColor.BOLD + error.message);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private List<String> handleTabCompletion(CommandManager.InternalCommand root, Command annotation, String[] args) {
+ try {
+ Set<Pair<CommandManager.InternalCommand.InternalCommandInvoker, Integer>> commands = new HashSet<>();
+ for (CommandManager.InternalCommand command : root.children) {
+ loopThroughCommandsTab(commands, 0, command, args);
+ }
+ if (!commands.isEmpty() || annotation.helpCommand()) {
+ List<Triple<CommandManager.InternalCommand.InternalCommandInvoker, Integer, Integer>> validCommands = new ArrayList<>(); // command, depth, and all processed params
+ for (Pair<CommandManager.InternalCommand.InternalCommandInvoker, Integer> pair : commands) {
+ CommandManager.InternalCommand.InternalCommandInvoker invoker = pair.getLeft();
+ int depth = pair.getRight();
+ int currentParam = 0;
+ boolean failed = false;
+ while (args.length - depth > 1) {
+ Parameter param = invoker.method.getParameters()[currentParam];
+ if (param.isAnnotationPresent(Greedy.class) && currentParam + 1 != invoker.parameterTypes.length) {
+ failed = true;
+ break;
+ }
+ ArgumentParser<?> parser = INSTANCE.parsers.get(param.getType());
+ if (parser == null) {
+ failed = true;
+ break;
+ }
+ try {
+ Arguments arguments = new Arguments(Arrays.copyOfRange(args, depth, args.length), param.isAnnotationPresent(Greedy.class));
+ if (parser.parse(arguments) != null) {
+ depth += arguments.getPosition();
+ currentParam++;
+ } else {
+ failed = true;
+ break;
+ }
+ } catch (Exception e) {
+ failed = true;
+ break;
+ }
+ }
+ if (!failed) {
+ validCommands.add(new ImmutableTriple<>(pair.getLeft(), depth, currentParam));
+ }
+ }
+ if (!validCommands.isEmpty() || annotation.helpCommand()) {
+ Set<String> completions = new HashSet<>();
+ for (Triple<CommandManager.InternalCommand.InternalCommandInvoker, Integer, Integer> valid : validCommands) {
+ if (valid.getMiddle() == args.length) {
+ completions.add(valid.getLeft().name);
+ completions.addAll(Arrays.asList(valid.getLeft().aliases));
+ continue;
+ }
+ if (valid.getRight() + 1 > valid.getLeft().parameterTypes.length) continue;
+ Parameter param = valid.getLeft().method.getParameters()[valid.getRight()];
+ if (param.isAnnotationPresent(Greedy.class) && valid.getRight() + 1 != valid.getLeft().parameterTypes.length) {
+ continue;
+ }
+ ArgumentParser<?> parser = INSTANCE.parsers.get(param.getType());
+ if (parser == null) {
+ continue;
+ }
+ try {
+ Arguments arguments = new Arguments(Arrays.copyOfRange(args, valid.getMiddle(), args.length), param.isAnnotationPresent(Greedy.class));
+ List<String> possibleCompletions = parser.complete(arguments, param);
+ if (possibleCompletions != null) {
+ completions.addAll(possibleCompletions);
+ }
+ } catch (Exception ignored) {
+
+ }
+ }
+ if (args.length == 1 && annotation.helpCommand()) {
+ if ("help".startsWith(args[0].toLowerCase(Locale.ENGLISH))) {
+ completions.add("help");
+ }
+ }
+ return new ArrayList<>(completions);
+ }
+ }
+ } catch (Exception ignored) {
+
+ }
+ return null;
+ }
+
+ private List<Object> getParametersForInvoker(CommandManager.InternalCommand.InternalCommandInvoker invoker, int depth, String[] args) {
+ List<Object> parameters = new ArrayList<>();
+ int processed = depth;
+ int currentParam = 0;
+ while (processed < args.length) {
+ Parameter param = invoker.method.getParameters()[currentParam];
+ if (param.isAnnotationPresent(Greedy.class) && currentParam + 1 != invoker.parameterTypes.length) {
+ return Collections.singletonList(new CommandManager.CustomError("Parsing failed: Greedy parameter must be the last one."));
+ }
+ ArgumentParser<?> parser = INSTANCE.parsers.get(param.getType());
+ if (parser == null) {
+ return Collections.singletonList(new CommandManager.CustomError("No parser for " + invoker.method.getParameterTypes()[currentParam].getSimpleName() + "! Please report this to the mod author."));
+ }
+ try {
+ Arguments arguments = new Arguments(Arrays.copyOfRange(args, processed, args.length), param.isAnnotationPresent(Greedy.class));
+ try {
+ Object a = parser.parse(arguments);
+ if (a != null) {
+ parameters.add(a);
+ processed += arguments.getPosition();
+ currentParam++;
+ } else {
+ return Collections.singletonList(new CommandManager.CustomError("Failed to parse " + param.getType().getSimpleName() + "! Please report this to the mod author."));
+ }
+ } catch (Exception e) {
+ return Collections.singletonList(new CommandManager.CustomError("A " + e.getClass().getSimpleName() + " has occured while try to parse " + param.getType().getSimpleName() + "! Please report this to the mod author."));
+ }
+ } catch (Exception e) {
+ return Collections.singletonList(new CommandManager.CustomError("A " + e.getClass().getSimpleName() + " has occured while try to parse " + param.getType().getSimpleName() + "! Please report this to the mod author."));
+ }
+ }
+ return parameters;
+ }
+
+ private int loopThroughCommands(List<CommandManager.InternalCommand.InternalCommandInvoker> commands, int depth, CommandManager.InternalCommand command, String[] args) {
+ int nextDepth = depth + 1;
+ boolean thatOneSpecialError = false;
+ if (command.isValid(args[depth], false)) {
+ for (CommandManager.InternalCommand child : command.children) {
+ if (args.length > nextDepth && child.isValid(args[nextDepth], false)) {
+ int result = loopThroughCommands(commands, nextDepth, child, args);
+ if (result > -1) {
+ return result;
+ } else if (result == -2) {
+ thatOneSpecialError = true;
+ }
+ }
+ }
+ boolean added = false;
+ for (CommandManager.InternalCommand.InternalCommandInvoker invoker : command.invokers) {
+ if (args.length - nextDepth == invoker.parameterTypes.length) {
+ commands.add(invoker);
+ added = true;
+ } else {
+ thatOneSpecialError = true;
+ }
+ }
+ if (added) {
+ return nextDepth;
+ }
+ }
+ return thatOneSpecialError ? -2 : -1;
+ }
+
+ private void loopThroughCommandsTab(Set<Pair<CommandManager.InternalCommand.InternalCommandInvoker, Integer>> commands, int depth, CommandManager.InternalCommand command, String[] args) {
+ int nextDepth = depth + 1;
+ if (command.isValid(args[depth], args.length == nextDepth)) {
+ if (args.length != nextDepth) {
+ for (CommandManager.InternalCommand child : command.children) {
+ if (child.isValid(args[nextDepth], args.length == nextDepth + 1)) {
+ loopThroughCommandsTab(commands, nextDepth, child, args);
+ }
+ }
+ }
+ for (CommandManager.InternalCommand.InternalCommandInvoker invoker : command.invokers) {
+ commands.add(new ImmutablePair<>(invoker, nextDepth));
+ }
+ }
+ }
+
+ //TODO: someone make the help command actually look nice lmao
+ private String sendHelpCommand(CommandManager.InternalCommand root) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(ChatColor.GOLD).append("Help for ").append(ChatColor.BOLD).append(root.name).append(ChatColor.RESET).append(ChatColor.GOLD).append(":\n");
+ if (!root.description.isEmpty()) {
+ builder.append("\n").append(ChatColor.GOLD).append("Description: ").append(ChatColor.BOLD).append(root.description);
+ }
+ for (CommandManager.InternalCommand command : root.children) {
+ runThroughCommandsHelp(root.name, command, builder);
+ }
+ builder.append("\n").append(ChatColor.GOLD).append("Aliases: ").append(ChatColor.BOLD);
+ int index = 0;
+ for (String alias : root.aliases) {
+ ++index;
+ builder.append(alias).append(index < root.aliases.length ? ", " : "");
+ }
+ builder.append("\n");
+ return builder.toString();
+ }
+
+ private void runThroughCommandsHelp(String append, CommandManager.InternalCommand command, StringBuilder builder) {
+ if (!command.invokers.isEmpty()) {
+ Class<?> declaringClass = command.invokers.get(0).method.getDeclaringClass();
+ if (declaringClass.isAnnotationPresent(SubCommand.class)) {
+ String description = declaringClass.getAnnotation(SubCommand.class).description();
+ if (!description.isEmpty()) {
+ builder.append("\n").append(ChatColor.GOLD).append("Description: ").append(ChatColor.BOLD).append(description);
+ }
+ }
+ }
+ for (CommandManager.InternalCommand.InternalCommandInvoker invoker : command.invokers) {
+ builder.append("\n").append(ChatColor.GOLD).append("/").append(append).append(" ").append(command.name);
+ for (Parameter parameter : invoker.method.getParameters()) {
+ String name = parameter.getName();
+ if (parameter.isAnnotationPresent(Name.class)) {
+ name = parameter.getAnnotation(Name.class).value();
+ }
+ builder.append(" <").append(name).append(">");
+ }
+ if (!command.description.trim().isEmpty()) {
+ builder.append(": ").append(ChatColor.BOLD).append(command.description);
+ }
+ }
+ for (CommandManager.InternalCommand subCommand : command.children) {
+ runThroughCommandsHelp(append + " " + command.name, subCommand, builder);
+ }
+ }
+} \ No newline at end of file
diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/utils/gui/GuiUtils.java b/versions/src/main/java/cc/polyfrost/oneconfig/utils/gui/GuiUtils.java
new file mode 100644
index 0000000..6217bb6
--- /dev/null
+++ b/versions/src/main/java/cc/polyfrost/oneconfig/utils/gui/GuiUtils.java
@@ -0,0 +1,100 @@
+package cc.polyfrost.oneconfig.utils.gui;
+
+import cc.polyfrost.oneconfig.events.EventManager;
+import cc.polyfrost.oneconfig.events.event.RenderEvent;
+import cc.polyfrost.oneconfig.events.event.Stage;
+import cc.polyfrost.oneconfig.gui.OneConfigGui;
+import cc.polyfrost.oneconfig.libs.eventbus.Subscribe;
+import cc.polyfrost.oneconfig.libs.universal.UMinecraft;
+import cc.polyfrost.oneconfig.libs.universal.UScreen;
+import cc.polyfrost.oneconfig.utils.TickDelay;
+import net.minecraft.client.gui.GuiScreen;
+
+import java.util.Deque;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+/**
+ * A class containing utility methods for working with GuiScreens.
+ */
+public final class GuiUtils {
+ private static long time = -1L;
+ private static long deltaTime = 17L;
+ private static final Deque<Optional<GuiScreen>> screenQueue = new ConcurrentLinkedDeque<>();
+
+ static {
+ EventManager.INSTANCE.register(new GuiUtils());
+ }
+
+ /**
+ * Displays a screen after a tick, preventing mouse sync issues.
+ *
+ * @param screen the screen to display.
+ * @deprecated Not actually deprecated, but should not be used.
+ */
+ @Deprecated
+ public static void displayScreen(Object screen) {
+ displayScreen(((GuiScreen) screen));
+ }
+
+ /**
+ * Displays a screen after a tick, preventing mouse sync issues.
+ *
+ * @param screen the screen to display.
+ */
+ public static void displayScreen(GuiScreen screen) {
+ //noinspection ConstantConditions
+ displayScreen(screen, screen instanceof OneConfigGui ? 2 : 1);
+ }
+
+ /**
+ * Displays a screen after the specified amount of ticks.
+ *
+ * @param screen the screen to display.
+ * @param ticks the amount of ticks to wait for before displaying the screen.
+ */
+ public static void displayScreen(GuiScreen screen, int ticks) {
+ Optional<GuiScreen> optional = Optional.of(screen);
+ screenQueue.add(optional);
+ new TickDelay(() -> {
+ UScreen.displayScreen(screen);
+ screenQueue.remove(optional);
+ }, ticks);
+ }
+
+ public static Deque<Optional<GuiScreen>> getScreenQueue() {
+ return screenQueue;
+ }
+
+ /**
+ * Close the current open GUI screen.
+ */
+ public static void closeScreen() {
+ UScreen.displayScreen(null);
+ }
+
+ /**
+ * Gets the delta time (in milliseconds) between frames.
+ * <p><b>
+ * Not to be confused with Minecraft deltaTicks / renderPartialTicks, which can be gotten via
+ * {@link cc.polyfrost.oneconfig.events.event.TimerUpdateEvent}
+ * </b></p>
+ *
+ * @return the delta time.
+ */
+ public static float getDeltaTime() {
+ return deltaTime;
+ }
+
+ @Subscribe
+ private void onRenderEvent(RenderEvent event) {
+ if (event.stage == Stage.START) {
+ if (time == -1) time = UMinecraft.getTime();
+ else {
+ long currentTime = UMinecraft.getTime();
+ deltaTime = currentTime - time;
+ time = currentTime;
+ }
+ }
+ }
+}