void setPrivateValue(Class super T> classToAccess, T instance, E value, String... fieldNames) {
+ try {
+ ReflectionHelper.setPrivateValue(classToAccess, instance, value,
+ ObfuscationReflectionHelper.remapFieldNames(classToAccess.getName(), fieldNames));
+ } catch (Throwable e) {
+ sendLog("No methods found for arguments: %s !", Arrays.toString(fieldNames));
+ }
+ }
+
+ private static void sendLog(String message, Object... formatting) {
+ if (logs.contains(message)) {
+ return;
+ }
+
+ System.out.println(String.format(message, formatting));
+
+ logs.add(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/backend/ThreadFactory.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/backend/ThreadFactory.java
new file mode 100644
index 0000000..3c849e0
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/backend/ThreadFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.backend;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ThreadFactory {
+
+ private AtomicInteger threadNumber = new AtomicInteger(0); // The current ThreadCount
+
+ private ExecutorService POOL; // Async task scheduler
+
+ public ThreadFactory(String factoryName) {
+ POOL = Executors.newFixedThreadPool(8, r -> new Thread(r, String.format("%s Thread %s", factoryName, this.threadNumber.incrementAndGet())));
+ }
+
+ /**
+ * Runs a task async to the main thread
+ *
+ * @param runnable the runnable to run
+ */
+ public void runAsync(Runnable runnable) {
+ this.POOL.execute(runnable);
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/game/ChatColor.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/game/ChatColor.java
new file mode 100644
index 0000000..da37f06
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/game/ChatColor.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.game;
+
+import java.util.regex.Pattern;
+
+public enum ChatColor {
+
+ BLACK('0'),
+ DARK_BLUE('1'),
+ DARK_GREEN('2'),
+ DARK_AQUA('3'),
+ DARK_RED('4'),
+ DARK_PURPLE('5'),
+ GOLD('6'),
+ GRAY('7'),
+ DARK_GRAY('8'),
+ BLUE('9'),
+ GREEN('a'),
+ AQUA('b'),
+ RED('c'),
+ LIGHT_PURPLE('d'),
+ YELLOW('e'),
+ WHITE('f'),
+ MAGIC('k', true),
+ BOLD('l', true),
+ STRIKETHROUGH('m', true),
+ UNDERLINE('n', true),
+ ITALIC('o', true),
+ RESET('r');
+
+ public static final char COLOR_CHAR = '\u00A7';
+
+ private final char code;
+ private final boolean isFormat;
+ private final String toString;
+
+ ChatColor(char code) {
+ this(code, false);
+ }
+
+ ChatColor(char code, boolean isFormat) {
+ this.code = code;
+ this.isFormat = isFormat;
+ this.toString = new String(new char[] {COLOR_CHAR, code});
+ }
+
+ public char getChar() {
+ return code;
+ }
+
+ @Override
+ public String toString() {
+ return toString;
+ }
+
+ public boolean isFormat() {
+ return isFormat;
+ }
+
+ public boolean isColor() {
+ return !isFormat && this != RESET;
+ }
+
+ public static String stripColor(final String input) {
+ if (input == null) {
+ return null;
+ }
+ return Pattern.compile("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]").matcher(input).replaceAll("");
+ }
+
+ public static String translateAlternateColorCodes(String textToTranslate) {
+ return translateAlternateColorCodes('&', textToTranslate);
+ }
+
+ public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
+ char[] b = textToTranslate.toCharArray();
+ for (int i = 0; i < b.length - 1; i++) {
+ if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) {
+ b[i] = ChatColor.COLOR_CHAR;
+ b[i+1] = Character.toLowerCase(b[i+1]);
+ }
+ }
+ return new String(b);
+ }
+
+ /*
+ * Color then strip the message, useful for removing all colors in the message.
+ */
+ public static String formatUnformat(char altColorChat, String message) {
+ return stripColor(translateAlternateColorCodes(altColorChat, message));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/boomboompower/skinchanger/utils/BetterJsonObject.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/BetterJsonObject.java
similarity index 74%
rename from src/main/java/me/boomboompower/skinchanger/utils/BetterJsonObject.java
rename to src/main/java/me/do_you_like/mods/skinchanger/utils/general/BetterJsonObject.java
index ec01ea9..3b5527d 100644
--- a/src/main/java/me/boomboompower/skinchanger/utils/BetterJsonObject.java
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/BetterJsonObject.java
@@ -1,31 +1,24 @@
/*
- * Hypixel Community Client, Client optimized for Hypixel Network
- * Copyright (C) 2018 Hyperium Dev Team
+ * Copyright (C) 2020 boomboompower
*
* This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package me.boomboompower.skinchanger.utils;
+package me.do_you_like.mods.skinchanger.utils.general;
+
+import com.google.gson.*;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonIOException;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSyntaxException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
@@ -33,20 +26,23 @@
/**
* The solution to the missing methods in Google's implementation
- * of json, this class contains many useful methods such as pretty
- * printing and file writing, as well as optional methods
+ * of json, this class contains many useful methods such as pretty
+ * printing and file writing, as well as optional methods
*
* @author boomboompower
* @version 1.0
*/
-@SuppressWarnings({"WeakerAccess", "ResultOfMethodCallIgnored", "UnusedReturnValue",
- "BooleanMethodIsAlwaysInverted"}) // Don't care
+@SuppressWarnings({"WeakerAccess", "ResultOfMethodCallIgnored", "UnusedReturnValue"}) // Don't care
public class BetterJsonObject {
- /** Our pretty printer */
+ /**
+ * Our pretty printer
+ */
private final Gson prettyPrinter = new GsonBuilder().setPrettyPrinting().create();
- /** The data holder for our json */
+ /**
+ * The data holder for our json
+ */
private JsonObject data;
/**
@@ -58,8 +54,8 @@ public BetterJsonObject() {
/**
* The default constructor the BetterJsonObject class, uses a json string
- * as the parameter and attempts to load it, a new JsonObject will be
- * created if the input is null, empty or cannot be loaded into a json format
+ * as the parameter and attempts to load it, a new JsonObject will be
+ * created if the input is null, empty or cannot be loaded into a json format
*
* @param jsonIn the json string to be parsed
*/
@@ -77,8 +73,8 @@ public BetterJsonObject(String jsonIn) {
/**
* The alternative constructor for the BetterJsonObject class, this uses
- * another JsonObject as the data set. A new JsonObject will be created
- * if the input is null
+ * another JsonObject as the data set. A new JsonObject will be created
+ * if the input is null
*
* @param objectIn the object to be used
*/
@@ -88,9 +84,9 @@ public BetterJsonObject(JsonObject objectIn) {
/**
* The optional string method, returns an empty string if
- * the key is null, empty or the data does not contain
- * the key. This will also return an empty string if the data value
- * is not a string
+ * the key is null, empty or the data does not contain
+ * the key. This will also return an empty string if the data value
+ * is not a string
*
* @param key the key the value will be loaded from
* @return the value in the json data set or empty if the key cannot be found
@@ -101,9 +97,9 @@ public String optString(String key) {
/**
* The optional string method, returns the default value if
- * the key is null, empty or the data does not contain
- * the key. This will also return the default value if
- * the data value is not a string
+ * the key is null, empty or the data does not contain
+ * the key. This will also return the default value if
+ * the data value is not a string
*
* @param key the key the value will be loaded from
* @return the value in the json data set or the default if the key cannot be found
@@ -123,9 +119,9 @@ public String optString(String key, String value) {
/**
* The optional int method, returns 0 if
- * the key is null, empty or the data does not contain
- * the key. This will also return 0 if the data value
- * is not a string
+ * the key is null, empty or the data does not contain
+ * the key. This will also return 0 if the data value
+ * is not a string
*
* @param key the key the value will be loaded from
* @return the value in the json data set or empty if the key cannot be found
@@ -136,9 +132,9 @@ public int optInt(String key) {
/**
* The optional int method, returns the default value if
- * the key is null, empty or the data does not contain
- * the key. This will also return the default value if
- * the data value is not a number
+ * the key is null, empty or the data does not contain
+ * the key. This will also return the default value if
+ * the data value is not a number
*
* @param key the key the value will be loaded from
* @return the value in the json data set or the default if the key cannot be found
@@ -161,9 +157,9 @@ public int optInt(String key, int value) {
/**
* The optional double method, returns 0.0D if
- * the key is null, empty or the data does not contain
- * the key. This will also return 0.0D if the data value
- * is not a string
+ * the key is null, empty or the data does not contain
+ * the key. This will also return 0.0D if the data value
+ * is not a string
*
* @param key the key the value will be loaded from
* @return the value in the json data set or empty if the key cannot be found
@@ -174,9 +170,9 @@ public double optDouble(String key) {
/**
* The optional double method, returns the default value if
- * the key is null, empty or the data does not contain
- * the key. This will also return the default value if
- * the data value is not a number
+ * the key is null, empty or the data does not contain
+ * the key. This will also return the default value if
+ * the data value is not a number
*
* @param key the key the value will be loaded from
* @return the value in the json data set or the default if the key cannot be found
@@ -199,9 +195,9 @@ public double optDouble(String key, double value) {
/**
* The optional boolean method, returns false if
- * the key is null, empty or the data does not contain
- * the key. This will also return false if the data value
- * is not a string
+ * the key is null, empty or the data does not contain
+ * the key. This will also return false if the data value
+ * is not a string
*
* @param key the key the value will be loaded from
* @return the value in the json data set or empty if the key cannot be found
@@ -212,9 +208,9 @@ public boolean optBoolean(String key) {
/**
* The optional boolean method, returns the default value if
- * the key is null, empty or the data does not contain
- * the key. This will also return the default value if
- * the data value is not a boolean
+ * the key is null, empty or the data does not contain
+ * the key. This will also return the default value if
+ * the data value is not a boolean
*
* @param key the key the value will be loaded from
* @return the value in the json data set or the default if the key cannot be found
@@ -251,9 +247,9 @@ public JsonObject getData() {
/**
* Adds a string to the to the json data file with the
- * key that it'll be associated with
+ * key that it'll be associated with
*
- * @param key the key
+ * @param key the key
* @param value the value
*/
public BetterJsonObject addProperty(String key, String value) {
@@ -265,9 +261,9 @@ public BetterJsonObject addProperty(String key, String value) {
/**
* Adds a number to the to the json data file with the
- * key that it'll be associated with
+ * key that it'll be associated with
*
- * @param key the key
+ * @param key the key
* @param value the value
*/
public BetterJsonObject addProperty(String key, Number value) {
@@ -279,9 +275,9 @@ public BetterJsonObject addProperty(String key, Number value) {
/**
* Adds a boolean to the to the json data file with the
- * key that it'll be associated with
+ * key that it'll be associated with
*
- * @param key the key
+ * @param key the key
* @param value the value
*/
public BetterJsonObject addProperty(String key, Boolean value) {
@@ -290,11 +286,11 @@ public BetterJsonObject addProperty(String key, Boolean value) {
}
return this;
}
-
+
/**
* Adds another BetterJsonObject into this one
*
- * @param key the key
+ * @param key the key
* @param object the object to add
*/
public BetterJsonObject add(String key, BetterJsonObject object) {
@@ -306,8 +302,8 @@ public BetterJsonObject add(String key, BetterJsonObject object) {
/**
* This feature is a HUGE WIP and may not work, it is safer
- * to use the toString method with a BufferedWriter instead
- *
+ * to use the toString method with a BufferedWriter instead
+ *
* We are not responsible for any overwritten files, please use this carefully
*
* @param file File to write to
@@ -340,13 +336,13 @@ public void writeToFile(File file) {
/**
* Converts the JsonElement to the JsonPrimitive class to allow for better
- * functionality
+ * functionality
*
* @param element the element to be transferred
* @return the JsonPrimitive instance or null if is not an instanceof the JsonPrimitive class
*/
private JsonPrimitive asPrimitive(JsonElement element) {
- return element != null && element instanceof JsonPrimitive ? (JsonPrimitive) element : null;
+ return element instanceof JsonPrimitive ? (JsonPrimitive) element : null;
}
/**
@@ -361,11 +357,15 @@ public String toString() {
/**
* Returns the pretty printed data String with
- * indents and other things
+ * indents and other things
*
* @return pretty printed data
*/
public String toPrettyString() {
return this.prettyPrinter.toJson(this.data);
}
+
+ public Gson getGsonData() {
+ return this.prettyPrinter;
+ }
}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/general/Prerequisites.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/Prerequisites.java
new file mode 100644
index 0000000..8973cb9
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/Prerequisites.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.general;
+
+/**
+ * Simple prerequisites for varies values in the code.
+ *
+ * Just used to validate input to ensure things flow smoothly
+ */
+@SuppressWarnings("ALL")
+public class Prerequisites {
+
+ private Prerequisites() {
+ }
+
+ /**
+ * Ensures a value is not null. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ */
+ public static void notNull(Object value) {
+ notNull(value, "Value was null.");
+ }
+
+ /**
+ * Ensures a value is not null. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ * @param errorMessage the message to send
+ */
+ public static void notNull(Object value, String errorMessage) {
+ if (value != null) {
+ return;
+ }
+
+ // Hit em with the fat exception
+ throw new IllegalArgumentException(errorMessage);
+ }
+
+ /**
+ * Ensures a value is not null or empty. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ */
+ public static void notNullOrEmpty(String value) {
+ notNullOrEmpty(value, "Value was either null or empty.");
+ }
+
+ /**
+ * Ensures a value is not null or empty. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ * @param errorMessage the message to send
+ */
+ public static void notNullOrEmpty(String value, String errorMessage) {
+ // If the value is not null and its not empty, return.
+ if (value != null && !value.isEmpty()) {
+ return;
+ }
+
+ // Hit em with the fat exception
+ throw new IllegalArgumentException(errorMessage);
+ }
+
+ /**
+ * Ensures a value (string) is not empty. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ */
+ public static void notEmpty(String value) {
+ notEmpty(value, "Value was empty.");
+ }
+
+ /**
+ * Ensures a value (string) is not empty. If it is, an error will be thrown.
+ *
+ * @param value the value to check
+ * @param errorMessage the message to send
+ */
+ public static void notEmpty(String value, String errorMessage) {
+ if (value == null || !value.isEmpty()) {
+ return;
+ }
+
+ // Hit em with the fat exception
+ throw new IllegalArgumentException(errorMessage);
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/general/ReflectUtils.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/ReflectUtils.java
new file mode 100644
index 0000000..05c99e3
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/general/ReflectUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.general;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
+import net.minecraftforge.fml.relauncher.ReflectionHelper;
+
+/**
+ * A nice reflection class, using a mix of forges ReflectionHandler and some
+ * of java's internal reflection implementations whilst remaining sun-free!
+ */
+public class ReflectUtils {
+
+ private static List logs = new ArrayList<>();
+
+ public static MethodHandle findMethod(Class clazz, String[] methodNames, Class>... methodTypes) {
+ final Method method = ReflectionHelper.findMethod(clazz, null, methodNames, methodTypes);
+
+ try {
+ return MethodHandles.lookup().unreflect(method);
+ } catch (Exception e) {
+ throw new ReflectionHelper.UnableToFindMethodException(methodNames, e);
+ }
+ }
+
+ public static void setPrivateValue(Class super T> classToAccess, T instance, E value, String... fieldNames) {
+ try {
+ ReflectionHelper.setPrivateValue(classToAccess, instance, value,
+ ObfuscationReflectionHelper.remapFieldNames(classToAccess.getName(), fieldNames));
+ } catch (Throwable e) {
+ sendLog("No methods found for arguments: %s !", Arrays.toString(fieldNames));
+ }
+ }
+
+ public static void getPrivateValue(Class super T> classToAccess, T instance, String... fieldNames) {
+ try {
+ ReflectionHelper.getPrivateValue(classToAccess, instance,
+ ObfuscationReflectionHelper.remapFieldNames(classToAccess.getName(), fieldNames));
+ } catch (Throwable e) {
+ sendLog("No methods found for arguments: %s !", Arrays.toString(fieldNames));
+ }
+ }
+
+ private static void sendLog(String message, Object... formatting) {
+ if (logs.contains(message)) {
+ return;
+ }
+
+ System.out.println(String.format(message, formatting));
+ logs.add(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/UISkeleton.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/UISkeleton.java
new file mode 100644
index 0000000..f4ba590
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/UISkeleton.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.gui;
+
+import me.do_you_like.mods.skinchanger.utils.gui.impl.ModernButton;
+
+public interface UISkeleton {
+
+ void onGuiOpen();
+
+ default void onGuiClose() {
+ }
+
+ default void preRender() {
+ }
+
+ void onRender(int mouseX, int mouseY, float partialTicks);
+
+ default void postRender() {
+ }
+
+ default void buttonPressed(ModernButton button) {
+ }
+
+ default void rightClicked(ModernButton button) {
+ }
+
+ default void onKeyTyped(int keyCode, char keyCharacter) {
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernButton.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernButton.java
new file mode 100644
index 0000000..0fe2e2e
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernButton.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.gui.impl;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.audio.PositionedSoundRecord;
+import net.minecraft.client.audio.SoundHandler;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+
+import java.awt.*;
+
+/**
+ * ModernButton, a nicer looking button
+ *
+ * @author boomboompower
+ * @version 2.0
+ */
+public class ModernButton extends Gui {
+
+ protected static final ResourceLocation buttonTextures = new ResourceLocation("textures/gui/widgets.png");
+
+ @Getter
+ private int id;
+
+ @Setter
+ @Getter
+ private int width;
+
+ private int height;
+ private int xPosition;
+ private int yPosition;
+
+ @Getter
+ private boolean enabled;
+
+ @Setter
+ @Getter
+ private boolean visible;
+
+ @Getter
+ @Setter
+ private boolean favourite;
+
+ @Getter
+ private boolean hovered;
+
+ @Getter
+ private String buttonId;
+ private String displayString;
+
+ private Color enabledColor = null;
+ private Color disabledColor = null;
+
+ @Getter
+ private Object buttonData;
+
+ public ModernButton(int buttonId, int x, int y, String buttonText) {
+ this(buttonId, "", x, y, 200, 20, buttonText);
+ }
+
+ public ModernButton(int buttonId, String idName, int x, int y, String buttonText) {
+ this(buttonId, idName, x, y, 200, 20, buttonText);
+ }
+
+ public ModernButton(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText) {
+ this(buttonId, "", x, y, widthIn, heightIn, buttonText);
+ }
+
+ public ModernButton(int buttonId, String idName, int x, int y, int widthIn, int heightIn, String buttonText) {
+ this.width = 200;
+ this.height = 20;
+ this.enabled = true;
+ this.visible = true;
+ this.id = buttonId;
+ this.xPosition = x;
+ this.yPosition = y;
+ this.width = widthIn;
+ this.height = heightIn;
+ this.buttonId = idName;
+ this.displayString = buttonText;
+ }
+
+ /**
+ * Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this button and 2 if it IS hovering over
+ * this button.
+ */
+ protected int getHoverState(boolean mouseOver) {
+ int i = 1;
+
+ if (!this.enabled) {
+ i = 0;
+ } else if (mouseOver) {
+ i = 2;
+ }
+ return i;
+ }
+
+ /**
+ * Draws this button to the screen.
+ */
+ public void drawButton(Minecraft mc, int mouseX, int mouseY) {
+ if (this.visible) {
+ FontRenderer fontrenderer = mc.fontRendererObj;
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height;
+ int i = this.getHoverState(this.hovered);
+
+ int j = 14737632;
+
+ boolean modern = true; //SkinChangerMod.getInstance().getConfigurationHandler().isModernButton();
+
+ if (modern) {
+ mc.getTextureManager().bindTexture(buttonTextures);
+
+ GlStateManager.enableBlend();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.blendFunc(770, 771);
+
+ this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, 46 + i * 20, this.width / 2, this.height);
+ this.drawTexturedModalRect(this.xPosition + this.width / 2, this.yPosition, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
+ } else {
+ if (this.enabled) {
+ drawRect(this.xPosition, this.yPosition, this.xPosition + this.width, this.yPosition + height, getEnabledColor().getRGB());
+ } else {
+ drawRect(this.xPosition, this.yPosition, this.xPosition + this.width, this.yPosition + height, getDisabledColor().getRGB());
+ }
+ }
+
+ if (!this.enabled) {
+ j = 10526880;
+ } else if (this.hovered) {
+ j = 16777120;
+ }
+
+ if (this.enabled && this.favourite) {
+ fontrenderer.drawString("\u2726", this.xPosition + this.width - fontrenderer.getStringWidth("\u2726") - 4, this.yPosition + ((fontrenderer.FONT_HEIGHT / 2) + 2), Color.ORANGE.getRGB());
+ }
+
+ fontrenderer.drawString(this.displayString, (this.xPosition + this.width / 2 - fontrenderer.getStringWidth(this.displayString) / 2), this.yPosition + (this.height - 8) / 2, j, modern);
+ }
+ }
+
+ public boolean mousePressed(Minecraft mc, int mouseX, int mouseY) {
+ return this.enabled && this.visible && mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height;
+ }
+
+ public void mouseReleased(int mouseX, int mouseY) {
+ }
+
+ public void playPressSound(SoundHandler soundHandlerIn) {
+ soundHandlerIn.playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
+ }
+
+ public Color getEnabledColor() {
+ return this.enabledColor == null ? new Color(255, 255, 255, 75) : this.enabledColor;
+ }
+
+ public ModernButton setEnabledColor(Color colorIn) {
+ this.enabledColor = colorIn;
+
+ return this;
+ }
+
+ public Color getDisabledColor() {
+ return this.disabledColor == null ? new Color(100, 100, 100, 75) : this.disabledColor;
+ }
+
+ public ModernButton setDisabledColor(Color colorIn) {
+ this.disabledColor = colorIn;
+
+ return this;
+ }
+
+ public String getText() {
+ return this.displayString;
+ }
+
+ public void setText(String text) {
+ this.displayString = text != null ? text : "";
+ }
+
+ public ModernButton setEnabled(boolean isEnabled) {
+ this.enabled = isEnabled;
+
+ return this;
+ }
+
+ public boolean hasButtonData() {
+ return this.buttonData != null;
+ }
+
+ public Object setButtonData(Object buttonData) {
+ this.buttonData = buttonData;
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernGui.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernGui.java
new file mode 100644
index 0000000..d73205e
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernGui.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.gui.impl;
+
+import com.google.common.collect.Lists;
+import java.awt.Color;
+import java.io.IOException;
+import java.util.List;
+import me.boomboompower.skinchanger.utils.ChatColor;
+import me.do_you_like.mods.skinchanger.SkinChangerMod;
+import me.do_you_like.mods.skinchanger.utils.gui.UISkeleton;
+import me.do_you_like.mods.skinchanger.utils.gui.lock.UILock;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiLabel;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.client.renderer.entity.RenderManager;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.util.ChatComponentText;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+/**
+ * ModernGui, a better-looking GuiScreen which has more optimizations and features than the normal GuiScreen
+ *
+ * @author boomboompower
+ * @version 4.0
+ */
+public abstract class ModernGui extends UILock implements UISkeleton {
+
+ protected final Minecraft mc = Minecraft.getMinecraft();
+ protected final FontRenderer fontRendererObj = this.mc.fontRendererObj;
+ protected final SkinChangerMod mod = SkinChangerMod.getInstance();
+
+ protected List textList = Lists.newArrayList();
+ protected List buttonList = Lists.newArrayList();
+
+ private ModernButton selectedButton;
+
+ @Override
+ public final void initGui() {
+ this.textList.clear();
+ this.buttonList.clear();
+
+ onGuiOpen();
+ }
+
+ @Override
+ public final void onGuiClosed() {
+ onGuiClose();
+ }
+
+ @Override
+ public final void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ try {
+ preRender();
+ } catch (Exception ex) {
+ drawCenteredString(this.fontRendererObj, "An error occurred during preRender();", 0, 0, Color.RED.getRGB());
+
+ for (int i = 0; i < ex.getStackTrace().length; i++) {
+ StackTraceElement element = ex.getStackTrace()[i];
+
+ drawCenteredString(this.fontRendererObj, element.toString(), 5, 12 + (i * 12), Color.RED.getRGB());
+ }
+
+ return;
+ }
+
+ try {
+ onRender(mouseX, mouseY, partialTicks);
+ } catch (Exception ex) {
+ drawCenteredString(this.fontRendererObj, "An error occurred during onRender();", 0, 0, Color.RED.getRGB());
+
+ for (int i = 0; i < ex.getStackTrace().length; i++) {
+ StackTraceElement element = ex.getStackTrace()[i];
+
+ drawCenteredString(this.fontRendererObj, element.toString(), 5, 12 + (i * 12), Color.RED.getRGB());
+ }
+
+ return;
+ }
+
+ for (ModernButton button : this.buttonList) {
+ button.drawButton(this.mc, mouseX, mouseY);
+ }
+
+ for (GuiLabel label : this.labelList) {
+ label.drawLabel(this.mc, mouseX, mouseY);
+ }
+
+ for (ModernTextBox text : this.textList) {
+ text.drawTextBox();
+ }
+
+ try {
+ postRender();
+ } catch (Exception ex) {
+ drawCenteredString(this.fontRendererObj, "An error occurred during postRender();", 0, 0, Color.RED.getRGB());
+
+ for (int i = 0; i < ex.getStackTrace().length; i++) {
+ StackTraceElement element = ex.getStackTrace()[i];
+
+ drawCenteredString(this.fontRendererObj, element.toString(), 5, 12 + (i * 12), Color.RED.getRGB());
+ }
+ }
+ }
+
+ @Override
+ protected final void keyTyped(char typedChar, int keyCode) {
+ onKeyTyped(keyCode, typedChar);
+
+ if (keyCode == 1) {
+ this.mc.displayGuiScreen(null);
+
+ if (this.mc.currentScreen == null) {
+ this.mc.setIngameFocus();
+ }
+ } else {
+ for (ModernTextBox text : textList) {
+ text.textboxKeyTyped(typedChar, keyCode);
+ }
+ }
+ }
+
+ @Override
+ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
+ if (mouseButton == 0) {
+ for (ModernButton button : this.buttonList) {
+ if (button.mousePressed(this.mc, mouseX, mouseY)) {
+ this.selectedButton = button;
+
+ button.playPressSound(this.mc.getSoundHandler());
+
+ this.buttonPressed(button);
+ }
+ }
+ }
+
+ if (mouseButton == 1) {
+ for (ModernButton button : this.buttonList) {
+ if (button != null) {
+ if (button.mousePressed(this.mc, mouseX, mouseY)) {
+ this.rightClicked(button);
+ }
+ }
+ }
+ }
+
+ for (ModernTextBox text : this.textList) {
+ text.mouseClicked(mouseX, mouseY, mouseButton);
+ }
+ }
+
+ @Override
+ public void setWorldAndResolution(Minecraft mc, int width, int height) {
+ // Required for compatibility
+ super.mc = mc;
+
+ this.itemRender = mc.getRenderItem();
+ this.width = width;
+ this.height = height;
+
+ this.textList.clear();
+ this.buttonList.clear();
+
+ initGui();
+ }
+
+ @Override
+ public final void updateScreen() {
+ for (ModernTextBox textBox : this.textList) {
+ textBox.updateCursorCounter();
+ }
+ }
+
+ @Override
+ protected final void mouseReleased(int mouseX, int mouseY, int state) {
+ if (this.selectedButton != null && state == 0) {
+ this.selectedButton.mouseReleased(mouseX, mouseY);
+ this.selectedButton = null;
+ }
+ }
+
+ /**
+ * Draws multiple lines on the screen
+ *
+ * @param startingX the starting x position of the text
+ * @param startingY the starting y position of the text
+ * @param separation the Y valye separatation between each line
+ * @param lines the lines which will be drawn
+ */
+ public void writeInformation(int startingX, int startingY, int separation, String... lines) {
+ writeInformation(startingX, startingY, separation, true, lines);
+ }
+
+ /**
+ * Draws multiple lines on the screen
+ *
+ * @param startingX the starting x position of the text
+ * @param startingY the starting y position of the text
+ * @param separation the Y valye separatation between each line
+ * @param centered true if the text being rendered should be rendered as a centered string
+ * @param lines the lines which will be drawn
+ */
+ public void writeInformation(int startingX, int startingY, int separation, boolean centered, String... lines) {
+ if (lines == null || lines.length == 0) {
+ return;
+ }
+
+ // Loop through the lines
+ for (String line : lines) {
+ // Null components will be treated as an empty string
+ if (line == null) {
+ line = "";
+ }
+
+ if (centered) {
+ drawCenteredString(this.fontRendererObj, ChatColor.translateAlternateColorCodes('&', line), startingX, startingY, Color.WHITE.getRGB());
+ } else {
+ drawString(this.fontRendererObj, ChatColor.translateAlternateColorCodes('&', line), startingX, startingY, Color.WHITE.getRGB());
+ }
+
+ startingY += separation;
+ }
+ }
+
+ public final void display() {
+ MinecraftForge.EVENT_BUS.register(this);
+ }
+
+ @SubscribeEvent
+ public final void onTick(TickEvent.ClientTickEvent event) {
+ MinecraftForge.EVENT_BUS.unregister(this);
+ Minecraft.getMinecraft().displayGuiScreen(this);
+ }
+
+ /**
+ * Code used to draw an entity on screen, stolen from the Inventory code
+ *
+ * @param posX the x position of the entity
+ * @param posY the y position of the entity
+ * @param scale the scale the entity should be rendered at
+ * @param mouseX the x location of the mouse
+ * @param mouseY the y location of the mouse
+ * @param entity the entity to render on screen
+ * @param previewCape true if the entities cape is being previewed
+ */
+ protected final void drawEntityOnScreen(int posX, int posY, int scale, float mouseX, float mouseY, EntityLivingBase entity, boolean previewCape) {
+ GlStateManager.enableColorMaterial();
+ GlStateManager.pushMatrix();
+ GlStateManager.translate((float) posX, (float) posY, 50.0F);
+ GlStateManager.scale((float) (-scale), (float) scale, (float) scale);
+ GlStateManager.rotate(180.0F, 0.0F, 0.0F , 1.0F);
+ if (previewCape) {
+ GlStateManager.rotate(180F, 0F, 360F, 0F);
+ }
+ float prevYawOffset = entity.renderYawOffset;
+ float prevYaw = entity.rotationYaw;
+ float prevPitch = entity.rotationPitch;
+ float prevYawRotation = entity.prevRotationYawHead;
+ float prevHeadRotation = entity.rotationYawHead;
+ GlStateManager.rotate(135.0F, 0.0F, 1.0F, 0.0F);
+ RenderHelper.enableStandardItemLighting();
+ GlStateManager.rotate(-135.0F, 0.0F, 1.0F, 0.0F);
+ GlStateManager.rotate(-((float) Math.atan((double) (mouseY / 40.0F))) * 20.0F, 1.0F, 0.0F, 0.0F);
+ entity.renderYawOffset = previewCape ? -(float) Math.atan((double) (mouseX / 40.0F)) * 20.0F : (float) Math.atan((double) (mouseX / 40.0F)) * 20.0F;
+ entity.rotationYaw = previewCape ? -(float) Math.atan((double) (mouseX / 40.0F)) * 40.0F : (float) Math.atan((double) (mouseX / 40.0F)) * 40.0F;
+ entity.rotationPitch = -((float) Math.atan((double) (mouseY / 40.0F))) * 20.0F;
+ entity.rotationYawHead = entity.rotationYaw;
+ entity.prevRotationYawHead = entity.rotationYaw;
+ GlStateManager.translate(0.0F, 0.0F, 0.0F);
+ RenderManager rendermanager = Minecraft.getMinecraft().getRenderManager();
+ rendermanager.setPlayerViewY(previewCape ? 180.0F : 0.0F);
+ rendermanager.setRenderShadow(false);
+ rendermanager.doRenderEntity(entity, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F, true);
+ rendermanager.setRenderShadow(true);
+ entity.renderYawOffset = prevYawOffset;
+ entity.rotationYaw = prevYaw;
+ entity.rotationPitch = prevPitch;
+ entity.prevRotationYawHead = prevYawRotation;
+ entity.rotationYawHead = prevHeadRotation;
+
+ GlStateManager.popMatrix();
+ RenderHelper.disableStandardItemLighting();
+ GlStateManager.disableRescaleNormal();
+ GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit);
+ GlStateManager.disableTexture2D();
+ GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit);
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernTextBox.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernTextBox.java
new file mode 100644
index 0000000..fcb36b0
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/impl/ModernTextBox.java
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.gui.impl;
+
+import java.awt.Color;
+import java.util.Timer;
+import java.util.TimerTask;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiPageButtonList;
+import net.minecraft.client.gui.GuiScreen;
+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.util.ChatAllowedCharacters;
+import net.minecraft.util.MathHelper;
+
+public class ModernTextBox extends Gui {
+
+ private String alertMessage = "";
+
+ private final int id;
+ private final FontRenderer fontRendererInstance;
+ public int xPosition;
+ public int yPosition;
+ /** The width of this text field. */
+ public int width;
+ public int height;
+ /** Has the current text being edited on the textbox. */
+ private String text = "";
+ private int maxStringLength = 16;
+ private int cursorCounter;
+ private boolean enableBackgroundDrawing = true;
+ /** if true the textbox can lose focus by clicking elsewhere on the screen */
+ private boolean canLoseFocus = true;
+ /** If this value is true along with isEnabled, keyTyped will process the keys. */
+ private boolean isFocused = false;
+ /** If this value is true along with isFocused, keyTyped will process the keys. */
+ private boolean isEnabled = true;
+ /** The current character index that should be used as start of the rendered text. */
+ private int lineScrollOffset;
+ private int cursorPosition;
+ /** other selection position, maybe the same as the cursor */
+ private int selectionEnd;
+ private int enabledColor = 14737632;
+ private int disabledColor = 7368816;
+ /** True if this textbox is visible */
+ private boolean visible = true;
+ private GuiPageButtonList.GuiResponder guiResponder;
+
+ private boolean onlyAllowNumbers = false;
+ private String noTextMessage = "Write here!";
+
+ private boolean running = false;
+
+ public ModernTextBox(int componentId, int x, int y, int width, int height) {
+ this(componentId, x, y, width, height, false);
+ }
+
+ public ModernTextBox(int componentId, int x, int y, int width, int height, boolean onlyAllowNumbers) {
+ this(componentId, x, y, width, height, "Write Here!", onlyAllowNumbers);
+ }
+
+ public ModernTextBox(int componentId, int x, int y, int width, int height, String noTextMessage, boolean onlyAllowNumbers) {
+ this.id = componentId;
+ this.fontRendererInstance = Minecraft.getMinecraft().fontRendererObj;
+ this.xPosition = x;
+ this.yPosition = y;
+ this.width = width;
+ this.height = height;
+ this.noTextMessage = noTextMessage;
+ this.onlyAllowNumbers = onlyAllowNumbers;
+ }
+
+ public void setGuiResponder(GuiPageButtonList.GuiResponder guiResponder) {
+ this.guiResponder = guiResponder;
+ }
+
+ /**
+ * Increments the cursor counter
+ */
+ public void updateCursorCounter() {
+ ++this.cursorCounter;
+ }
+
+ /**
+ * Sets the text of the textbox
+ */
+ public void setText(String text) {
+ if (text == null) {
+ this.text = "";
+ this.setCursorPositionEnd();
+ return;
+ }
+
+ if (format(text).length() > this.maxStringLength) {
+ this.text = format(text).substring(0, this.maxStringLength);
+ } else {
+ this.text = format(text);
+ }
+ this.setCursorPositionEnd();
+ }
+
+ /**
+ * Returns the contents of the textbox
+ */
+ public String getText() {
+ return this.text;
+ }
+
+ /**
+ * returns the text between the cursor and selectionEnd
+ */
+ public String getSelectedText() {
+ int i = this.cursorPosition < this.selectionEnd ? this.cursorPosition: this.selectionEnd;
+ int j = this.cursorPosition < this.selectionEnd ? this.selectionEnd: this.cursorPosition;
+ return this.text.substring(i, j);
+ }
+
+ /**
+ * replaces selected text, or inserts text at the position on the cursor
+ */
+ public void writeText(String text) {
+ String s = "";
+ String s1 = ChatAllowedCharacters.filterAllowedCharacters(text);
+ int i = this.cursorPosition < this.selectionEnd ? this.cursorPosition: this.selectionEnd;
+ int j = this.cursorPosition < this.selectionEnd ? this.selectionEnd: this.cursorPosition;
+ int k = this.maxStringLength - this.text.length() - (i - j);
+ int l;
+
+ if (this.text.length() > 0) {
+ s = s + this.text.substring(0, i);
+ }
+
+ if (k < s1.length()) {
+ s = s + s1.substring(0, k);
+ l = k;
+ } else {
+ s = s + s1;
+ l = s1.length();
+ }
+
+ if (this.text.length() > 0 && j < this.text.length()) {
+ s = s + this.text.substring(j);
+ }
+
+ this.text = s;
+ this.moveCursorBy(i - this.selectionEnd + l);
+
+ if (this.guiResponder != null) {
+ this.guiResponder.func_175319_a(this.id, this.text);
+ }
+ }
+
+ /**
+ * Deletes the specified number of words starting at the cursor position. Negative numbers will delete words left of
+ * the cursor.
+ */
+ public void deleteWords(int words) {
+ if (this.text.length() != 0) {
+ if (this.selectionEnd != this.cursorPosition) {
+ this.writeText("");
+ } else {
+ this.deleteFromCursor(this.getNthWordFromCursor(words) - this.cursorPosition);
+ }
+ }
+ }
+
+ /**
+ * delete the selected text, otherwsie deletes characters from either side of the cursor. params: delete num
+ */
+ public void deleteFromCursor(int num) {
+ if (this.text.length() != 0) {
+ if (this.selectionEnd != this.cursorPosition) {
+ this.writeText("");
+ } else {
+ boolean flag = num < 0;
+ int i = flag ? this.cursorPosition + num: this.cursorPosition;
+ int j = flag ? this.cursorPosition: this.cursorPosition + num;
+ String s = "";
+
+ if (i >= 0) {
+ s = this.text.substring(0, i);
+ }
+
+ if (j < this.text.length()) {
+ s = s + this.text.substring(j);
+ }
+
+ this.text = s;
+
+ if (flag) {
+ this.moveCursorBy(num);
+ }
+
+ if (this.guiResponder != null) {
+ this.guiResponder.func_175319_a(this.id, this.text);
+ }
+ }
+ }
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ /**
+ * see @getNthNextWordFromPos() params: N, position
+ */
+ public int getNthWordFromCursor(int position) {
+ return this.getNthWordFromPos(position, this.getCursorPosition());
+ }
+
+ /**
+ * gets the position of the nth word. N may be negative, then it looks backwards. params: N, position
+ */
+ public int getNthWordFromPos(int number, int position) {
+ return this.getNthWordFromPos(number, position, true);
+ }
+
+ public int getNthWordFromPos(int number, int position, boolean something) {
+ int i = position;
+ boolean flag = number < 0;
+ int j = Math.abs(number);
+
+ for (int k = 0; k < j; ++k) {
+ if (!flag) {
+ int l = this.text.length();
+ i = this.text.indexOf(32, i);
+
+ if (i == -1) {
+ i = l;
+ } else {
+ while (something && i < l && this.text.charAt(i) == 32) {
+ ++i;
+ }
+ }
+ } else {
+ while (something && i > 0 && this.text.charAt(i -1) == 32) {
+ --i;
+ }
+
+ while (i > 0 && this.text.charAt(i -1) != 32) {
+ --i;
+ }
+ }
+ }
+
+ return i;
+ }
+
+ /**
+ * Moves the text cursor by a specified number of characters and clears the selection
+ */
+ public void moveCursorBy(int moveBy) {
+ this.setCursorPosition(this.selectionEnd + moveBy);
+ }
+
+ /**
+ * sets the position of the cursor to the provided index
+ */
+ public void setCursorPosition(int position) {
+ this.cursorPosition = position;
+ int i = this.text.length();
+ this.cursorPosition = MathHelper.clamp_int(this.cursorPosition, 0, i);
+ this.setSelectionPos(this.cursorPosition);
+ }
+
+ /**
+ * sets the cursors position to the beginning
+ */
+ public void setCursorPositionZero() {
+ this.setCursorPosition(0);
+ }
+
+ /**
+ * sets the cursors position to after the text
+ */
+ public void setCursorPositionEnd() {
+ this.setCursorPosition(this.text.length());
+ }
+
+ /**
+ * Call this method from your GuiScreen to process the keys into the textbox
+ */
+ public boolean textboxKeyTyped(char c, int keyCode) {
+ if (!this.isFocused || this.running) {
+ return false;
+ } else if (GuiScreen.isKeyComboCtrlA(keyCode)) {
+ this.setCursorPositionEnd();
+ this.setSelectionPos(0);
+ return true;
+ } else if (GuiScreen.isKeyComboCtrlC(keyCode)) {
+ GuiScreen.setClipboardString(this.getSelectedText());
+ return true;
+ } else if (GuiScreen.isKeyComboCtrlV(keyCode)) {
+ if (this.isEnabled) {
+ this.writeText(format(GuiScreen.getClipboardString()));
+ }
+
+ return true;
+ } else if (GuiScreen.isKeyComboCtrlX(keyCode)) {
+ GuiScreen.setClipboardString(this.getSelectedText());
+
+ if (this.isEnabled) {
+ this.writeText("");
+ }
+ return true;
+ } else {
+ switch (keyCode) {
+ case 14:
+ if (GuiScreen.isCtrlKeyDown()) {
+ if (this.isEnabled) {
+ this.deleteWords( -1);
+ }
+ }
+ else if (this.isEnabled) {
+ this.deleteFromCursor( -1);
+ }
+
+ return true;
+ case 199:
+ if (GuiScreen.isShiftKeyDown()) {
+ this.setSelectionPos(0);
+ } else {
+ this.setCursorPositionZero();
+ }
+ return true;
+ case 203:
+ if (GuiScreen.isShiftKeyDown()) {
+ if (GuiScreen.isCtrlKeyDown()) {
+ this.setSelectionPos(this.getNthWordFromPos( -1, this.getSelectionEnd()));
+ }
+ else {
+ this.setSelectionPos(this.getSelectionEnd() -1);
+ }
+ }
+ else if (GuiScreen.isCtrlKeyDown()) {
+ this.setCursorPosition(this.getNthWordFromCursor( -1));
+ }
+ else {
+ this.moveCursorBy( -1);
+ }
+
+ return true;
+ case 205:
+ if (GuiScreen.isShiftKeyDown()) {
+ if (GuiScreen.isCtrlKeyDown()) {
+ this.setSelectionPos(this.getNthWordFromPos(1, this.getSelectionEnd()));
+ } else {
+ this.setSelectionPos(this.getSelectionEnd() + 1);
+ }
+ } else if (GuiScreen.isCtrlKeyDown()) {
+ this.setCursorPosition(this.getNthWordFromCursor(1));
+ } else {
+ this.moveCursorBy(1);
+ }
+ return true;
+ case 207:
+ if (GuiScreen.isShiftKeyDown()) {
+ this.setSelectionPos(this.text.length());
+ } else {
+ this.setCursorPositionEnd();
+ }
+ return true;
+ case 211:
+ if (GuiScreen.isCtrlKeyDown()) {
+ if (this.isEnabled) {
+ this.deleteWords(1);
+ }
+ } else if (this.isEnabled) {
+ this.deleteFromCursor(1);
+ }
+ return true;
+ default:
+ if (ChatAllowedCharacters.isAllowedCharacter(c)) {
+ if (onlyAllowNumbers) {
+ if (Character.isDigit(c)) {
+ if (this.isEnabled) {
+ this.writeText(Character.toString(c));
+ }
+ } else if (!running) {
+ alert("Only numbers can be used!", 1250);
+ }
+ } else {
+ if (this.isEnabled) {
+ this.writeText(Character.toString(c));
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Args: x, y, buttonClicked
+ */
+ public void mouseClicked(int x, int y, int buttonClicked) {
+ boolean flag = x >= this.xPosition && x < this.xPosition + this.width && y >= this.yPosition && y < this.yPosition + this.height;
+
+ if (this.canLoseFocus && this.isEnabled && !this.running) {
+ this.setFocused(flag);
+ }
+
+ if (this.isFocused && flag && buttonClicked == 0) {
+ int i = x - this.xPosition;
+
+ if (this.enableBackgroundDrawing) {
+ i -= 4;
+ }
+
+ String s = this.fontRendererInstance.trimStringToWidth(this.text.substring(this.lineScrollOffset), this.getWidth());
+ this.setCursorPosition(this.fontRendererInstance.trimStringToWidth(s, i).length() + this.lineScrollOffset);
+ }
+ }
+
+ /**
+ * Draws the textbox
+ */
+ public void drawTextBox() {
+ if (this.getVisible()) {
+ if (this.getEnableBackgroundDrawing()) {
+ if (this.isEnabled) {
+ drawRect(this.xPosition, this.yPosition, this.xPosition + width, this.yPosition + height, new Color(0, 148, 255, 75).getRGB());
+ } else {
+ drawRect(this.xPosition, this.yPosition, this.xPosition + width, this.yPosition + height, new Color(0, 125, 215, 75).getRGB());
+ }
+ }
+
+ int i = this.isEnabled ? this.enabledColor : this.disabledColor;
+ int j = this.cursorPosition - this.lineScrollOffset;
+ int k = this.selectionEnd - this.lineScrollOffset;
+ String s = this.fontRendererInstance.trimStringToWidth(this.text.substring(this.lineScrollOffset), this.getWidth());
+ boolean flag = j >= 0 && j <= s.length();
+ boolean flag1 = this.isFocused && this.cursorCounter / 6 % 2 == 0 && flag;
+ int l = this.enableBackgroundDrawing ? this.xPosition + 4 : this.xPosition;
+ int i1 = this.enableBackgroundDrawing ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
+ int j1 = l;
+
+ if (k > s.length()) {
+ k = s.length();
+ }
+
+ if (!alertMessage.isEmpty()) {
+ this.fontRendererInstance.drawString(alertMessage, ((this.xPosition + this.width / 2) - fontRendererInstance.getStringWidth(alertMessage) / 2), this.yPosition + this.height / 2 - 4, enabledColor, false);
+ return;
+ }
+
+ if (s.isEmpty() && !isFocused && isEnabled) {
+ this.fontRendererInstance.drawString(noTextMessage, ((this.xPosition + this.width / 2) - fontRendererInstance.getStringWidth(noTextMessage) / 2), this.yPosition + this.height / 2 - 4, i, false);
+ return;
+ }
+
+ if (s.length() > 0) {
+ String s1 = flag ? s.substring(0, j) : s;
+ j1 = this.fontRendererInstance.drawString(s1, (float) l, (float) i1, i, false);
+ }
+
+ boolean flag2 = this.cursorPosition < this.text.length() || this.text.length() >= this.getMaxStringLength();
+ int k1 = j1;
+
+ if (!flag) {
+ k1 = j > 0 ? l + this.width: l;
+ } else if (flag2) {
+ k1 = j1 -1; --j1;
+ }
+
+ if (s.length() > 0 && flag && j < s.length()) {
+ this.fontRendererInstance.drawString(s.substring(j), (float) j1, (float) i1, i, false);
+ }
+
+ if (flag1) {
+ if (flag2) {
+ Gui.drawRect(k1, i1 -1, k1 + 1, i1 + 1 + this.fontRendererInstance.FONT_HEIGHT, -3092272);
+ }
+ else {
+ this.fontRendererInstance.drawString("_", (float) k1, (float) i1, i, false);
+ }
+ }
+
+ if (k != j) {
+ int l1 = l + this.fontRendererInstance.getStringWidth(s.substring(0, k));
+ this.drawCursorVertical(k1, i1 -1, l1 -1, i1 + 1 + this.fontRendererInstance.FONT_HEIGHT);
+ }
+ }
+ }
+
+ /**
+ * draws the vertical line cursor in the textbox
+ */
+ private void drawCursorVertical(int p_146188_1_, int p_146188_2_, int p_146188_3_, int p_146188_4_) {
+ if (p_146188_1_ < p_146188_3_) {
+ int i = p_146188_1_;
+ p_146188_1_ = p_146188_3_;
+ p_146188_3_ = i;
+ }
+
+ if (p_146188_2_ < p_146188_4_) {
+ int j = p_146188_2_;
+ p_146188_2_ = p_146188_4_;
+ p_146188_4_ = j;
+ }
+
+ if (p_146188_3_ > this.xPosition + this.width) {
+ p_146188_3_ = this.xPosition + this.width;
+ }
+
+ if (p_146188_1_ > this.xPosition + this.width) {
+ p_146188_1_ = this.xPosition + this.width;
+ }
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ GlStateManager.color(0.0F, 0.0F, 255.0F, 255.0F);
+ GlStateManager.disableTexture2D();
+ GlStateManager.enableColorLogic();
+ GlStateManager.colorLogicOp(5387);
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION);
+ worldrenderer.pos((double) p_146188_1_, (double) p_146188_4_, 0.0D).endVertex();
+ worldrenderer.pos((double) p_146188_3_, (double) p_146188_4_, 0.0D).endVertex();
+ worldrenderer.pos((double) p_146188_3_, (double) p_146188_2_, 0.0D).endVertex();
+ worldrenderer.pos((double) p_146188_1_, (double) p_146188_2_, 0.0D).endVertex();
+ tessellator.draw();
+ GlStateManager.disableColorLogic();
+ GlStateManager.enableTexture2D();
+ }
+
+ public void setMaxStringLength(int length) {
+ this.maxStringLength = length;
+
+ if (this.text.length() > length) {
+ this.text = this.text.substring(0, length);
+ }
+ }
+
+ /**
+ * returns the maximum number of character that can be contained in this textbox
+ */
+ public int getMaxStringLength() {
+ return this.maxStringLength;
+ }
+
+ /**
+ * returns the current position of the cursor
+ */
+ public int getCursorPosition() {
+ return this.cursorPosition;
+ }
+
+ /**
+ * get enable drawing background and outline
+ */
+ public boolean getEnableBackgroundDrawing() {
+ return this.enableBackgroundDrawing;
+ }
+
+ /**
+ * enable drawing background and outline
+ */
+ public void setEnableBackgroundDrawing(boolean enableBackgroundDrawing) {
+ this.enableBackgroundDrawing = enableBackgroundDrawing;
+ }
+
+ /**
+ * Sets the text colour for this textbox (disabled text will not use this colour)
+ */
+ public void setTextColor(int color) {
+ this.enabledColor = color;
+ }
+
+ public void setDisabledTextColour(int color) {
+ this.disabledColor = color;
+ }
+
+ /**
+ * Sets focus to this gui element
+ */
+ public void setFocused(boolean isFocused) {
+ if (isFocused && !this.isFocused) {
+ this.cursorCounter = 0;
+ }
+ this.isFocused = isFocused;
+ }
+
+ /**
+ * Getter for the focused field
+ */
+ public boolean isFocused() {
+ return this.isFocused;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.isEnabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return this.isEnabled;
+ }
+
+ /**
+ * the side of the selection that is not the cursor, may be the same as the cursor
+ */
+ public int getSelectionEnd() {
+ return this.selectionEnd;
+ }
+
+ /**
+ * returns the width of the textbox depending on if background drawing is enabled
+ */
+ public int getWidth() {
+ return this.getEnableBackgroundDrawing() ? this.width - 8 : this.width;
+ }
+
+ /**
+ * Sets the position of the selection anchor (i.e. position the selection was started at)
+ */
+ public void setSelectionPos(int pos) {
+ int i = this.text.length();
+
+ if (pos > i) {
+ pos = i;
+ }
+ if (pos < 0) {
+ pos = 0;
+ }
+
+ this.selectionEnd = pos;
+
+ if (this.fontRendererInstance != null) {
+ if (this.lineScrollOffset > i) {
+ this.lineScrollOffset = i;
+ }
+
+ int j = this.getWidth();
+ String s = this.fontRendererInstance.trimStringToWidth(this.text.substring(this.lineScrollOffset), j);
+ int k = s.length() + this.lineScrollOffset;
+
+ if (pos == this.lineScrollOffset) {
+ this.lineScrollOffset -= this.fontRendererInstance.trimStringToWidth(this.text, j, true).length();
+ }
+
+ if (pos > k) {
+ this.lineScrollOffset += pos - k;
+ } else if (pos <= this.lineScrollOffset) {
+ this.lineScrollOffset -= this.lineScrollOffset - pos;
+ }
+ this.lineScrollOffset = MathHelper.clamp_int(this.lineScrollOffset, 0, i);
+ }
+ }
+
+ /**
+ * if true the textbox can lose focus by clicking elsewhere on the screen
+ */
+ public void setCanLoseFocus(boolean canLoseFocus) {
+ this.canLoseFocus = canLoseFocus;
+ }
+
+ /**
+ * returns true if this textbox is visible
+ */
+ public boolean getVisible() {
+ return this.visible;
+ }
+
+ /**
+ * Sets whether or not this textbox is visible
+ */
+ public void setVisible(boolean isVisible) {
+ this.visible = isVisible;
+ }
+
+ public boolean isRunning() {
+ return this.running;
+ }
+
+ public void alert(String message, int time) {
+ if (message.isEmpty()) return;
+
+ running = true;
+ alertMessage = message;
+
+ new Timer().schedule(new TimerTask() {
+ @Override
+ public void run() {
+ alertMessage = "";
+ running = false;
+ }
+ }, time);
+ }
+
+ public void setOnlyAllowNumbers(boolean onlyAllowNumbers) {
+ this.onlyAllowNumbers = onlyAllowNumbers;
+ }
+
+ public boolean onlyAllowNumbers() {
+ return this.onlyAllowNumbers;
+ }
+
+ private String format(String input) {
+ if (this.onlyAllowNumbers) {
+ StringBuilder builder = new StringBuilder();
+ for (char c : input.toCharArray()) {
+ if (Character.isDigit(c)) {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ } else {
+ return input;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/lock/UILock.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/lock/UILock.java
new file mode 100644
index 0000000..95060d7
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/gui/lock/UILock.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.gui.lock;
+
+import java.awt.Color;
+import java.io.IOException;
+
+import me.boomboompower.skinchanger.utils.ChatColor;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.util.ChatComponentText;
+
+/**
+ * Locks the UI methods. Declutters the subclasses method list.
+ */
+public class UILock extends GuiScreen {
+
+ @Override
+ public final void drawDefaultBackground() {
+ Gui.drawRect(0, 0, this.width, this.height, new Color(2, 2, 2, 120).getRGB());
+ }
+
+ @Override
+ public final void drawCenteredString(FontRenderer fontRendererIn, String text, int x, int y, int color) {
+ fontRendererIn.drawString(text, (float) (x - fontRendererIn.getStringWidth(text) / 2), (float) y, color, false);
+ }
+
+ public final void drawCenteredString(FontRenderer fontRendererIn, String text, int x, int y, int color, boolean shadow) {
+ fontRendererIn.drawString(text, (float) (x - fontRendererIn.getStringWidth(text) / 2), (float) y, color, shadow);
+ }
+
+ @Override
+ public final void drawString(FontRenderer fontRendererIn, String text, int x, int y, int color) {
+ fontRendererIn.drawString(text, (float) x, (float) y, color, false);
+ }
+
+ @Override
+ public final boolean doesGuiPauseGame() {
+ return false;
+ }
+
+ @Override
+ public final void sendChatMessage(String msg) {
+ this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(
+ ChatColor.AQUA + "SkinChanger" + ChatColor.GOLD + " > " + ChatColor.GRAY + msg));
+ }
+
+ @Override
+ public final void confirmClicked(boolean result, int id) {
+ }
+
+ @Override
+ public final void sendChatMessage(String msg, boolean addToChat) {
+ sendChatMessage(msg);
+ }
+
+ @Override
+ public final void drawBackground(int tint) {
+ super.drawBackground(tint);
+ }
+
+ @Override
+ public final void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height) {
+ super.drawTexturedModalRect(x, y, textureX, textureY, width, height);
+ }
+
+ @Override
+ public final void drawTexturedModalRect(float xCoord, float yCoord, int minU, int minV, int maxU, int maxV) {
+ super.drawTexturedModalRect(xCoord, yCoord, minU, minV, maxU, maxV);
+ }
+
+ @Override
+ public final void drawTexturedModalRect(int xCoord, int yCoord, TextureAtlasSprite textureSprite, int widthIn, int heightIn) {
+ super.drawTexturedModalRect(xCoord, yCoord, textureSprite, widthIn, heightIn);
+ }
+
+ @Override
+ public final void drawWorldBackground(int tint) {
+ super.drawWorldBackground(tint);
+ }
+
+ @Override
+ public final void handleInput() throws IOException {
+ super.handleInput();
+ }
+
+ @Override
+ public final void handleKeyboardInput() throws IOException {
+ super.handleKeyboardInput();
+ }
+
+ @Override
+ public final void handleMouseInput() throws IOException {
+ super.handleMouseInput();
+ }
+
+ @Override
+ public final void onResize(Minecraft mcIn, int w, int h) {
+ super.onResize(mcIn, w, h);
+ }
+
+ @Override
+ protected final void actionPerformed(GuiButton button) {
+ }
+
+ @Override
+ protected final void setText(String newChatText, boolean shouldOverwrite) {
+ }
+
+ @Override
+ protected final void drawCreativeTabHoveringText(String tabName, int mouseX, int mouseY) {
+ }
+
+ @Override
+ protected final void drawGradientRect(int left, int top, int right, int bottom, int startColor, int endColor) {
+ super.drawGradientRect(left, top, right, bottom, startColor, endColor);
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OSType.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OSType.java
new file mode 100644
index 0000000..9949082
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OSType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.installing;
+
+public enum OSType {
+ WINDOWS("C:\\Users\\USERNAME\\AppData\\Roaming\\.minecraft"),
+ MAC("~/Library/Application Support/minecraft"),
+ LINUX("~/.minecraft"),
+ UNKNOWN("?");
+
+ private String normalDirectory;
+
+ OSType(String directory) {
+ this.normalDirectory = directory;
+ }
+
+ public String getNormalDirectory() {
+ return this.normalDirectory;
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OperatingSystem.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OperatingSystem.java
new file mode 100644
index 0000000..91ef89f
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/installing/OperatingSystem.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.installing;
+
+import java.io.File;
+
+public class OperatingSystem {
+
+ private static OSType osType;
+
+ static {
+ String osName = System.getProperty("os.name");
+
+ if (osName.startsWith("Windows")) {
+ osType = OSType.WINDOWS;
+ } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
+ osType = OSType.MAC;
+ } else if (osName.startsWith("Linux")) {
+ osType = OSType.LINUX;
+ } else {
+ osType = OSType.UNKNOWN;
+ }
+ }
+
+ private OperatingSystem() {
+ }
+
+ public static OSType getOSType() {
+ if (osType != null) {
+ return osType;
+ }
+
+ String osName = System.getProperty("os.name");
+
+ if (osName.startsWith("Windows")) {
+ osType = OSType.WINDOWS;
+ } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
+ osType = OSType.MAC;
+ } else if (osName.startsWith("Linux")) {
+ osType = OSType.LINUX;
+ } else {
+ osType = OSType.UNKNOWN;
+ }
+
+ return osType;
+ }
+
+ public static File getMinecraftDirectory(OSType system) {
+ String path;
+
+ switch (system) {
+ case WINDOWS:
+ path = "C:\\Users\\" + System.getProperty("user.name") + "\\AppData\\Roaming\\.minecraft\\";
+ break;
+ case LINUX:
+ case MAC:
+ path = system.getNormalDirectory();
+ break;
+ default:
+ path = null;
+ }
+
+ if (path == null) {
+ return null;
+ }
+
+ return new File(path);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/CacheRetriever.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/CacheRetriever.java
new file mode 100644
index 0000000..465d039
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/CacheRetriever.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.mod;
+
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.UUID;
+
+import me.do_you_like.mods.skinchanger.SkinChangerMod;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.IImageBuffer;
+import net.minecraft.client.renderer.ImageBufferDownload;
+import net.minecraft.client.renderer.ThreadDownloadImageData;
+import net.minecraft.client.resources.DefaultPlayerSkin;
+import net.minecraft.util.ResourceLocation;
+
+public class CacheRetriever {
+
+ private HashMap cachedValues = new HashMap<>();
+
+ private final SkinChangerMod mod;
+ private final File cacheDirectory;
+
+ public CacheRetriever(SkinChangerMod mod) {
+ this.mod = mod;
+ this.cacheDirectory = new File(mod.getModConfigDirectory(), "cache");
+
+ genCacheDirectory();
+ }
+
+ // String.format("https://minotar.net/skin/%s", name)
+ public ResourceLocation loadIntoGame(String name, String url) {
+ File cacheDirectory = getCacheDirForName(name);
+
+ boolean cacheFileExists = doesCacheExist(name);
+
+ if (isCacheExpired(name)) {
+ cacheFileExists = false;
+
+ cacheDirectory.delete();
+ }
+
+ ResourceLocation location = new ResourceLocation("skins/" + getCacheName(name));
+
+ File dataFile = new File(cacheDirectory, cacheDirectory.getName() + ".png");
+
+ IImageBuffer imageBuffer = new ImageBufferDownload();
+
+ ThreadDownloadImageData imageData = new ThreadDownloadImageData(dataFile, url, DefaultPlayerSkin.getDefaultSkinLegacy(), new IImageBuffer() {
+ @Override
+ public BufferedImage parseUserSkin(BufferedImage image) {
+ return imageBuffer.parseUserSkin(image);
+ }
+
+ @Override
+ public void skinAvailable() {
+ imageBuffer.skinAvailable();
+ }
+ });
+
+ Minecraft.getMinecraft().renderEngine.loadTexture(location, imageData);
+
+ if (!cacheFileExists) {
+ generateCacheFiles(name);
+ }
+
+ return location;
+ }
+
+ public void generateCacheFiles(String name) {
+ File cacheDirectory = getCacheDirForName(name);
+
+ if (isCacheExpired(name)) {
+ cacheDirectory.delete();
+ }
+
+ if (!cacheDirectory.exists()) {
+ if (!cacheDirectory.mkdir()) {
+ System.err.println("Failed to create a cache directory.");
+ return;
+ }
+ }
+
+ File dataFile = new File(cacheDirectory, cacheDirectory.getName() + ".png");
+ File cacheFile = new File(cacheDirectory, cacheDirectory.getName() + ".lock");
+
+ try {
+ if (!dataFile.exists()) {
+ if (!dataFile.createNewFile()) {
+ System.err.println("Failed to create a cache file.");
+ }
+ }
+
+ if (!cacheFile.exists()) {
+ if (!cacheFile.createNewFile()) {
+ System.err.println("Failed to create a cache file.");
+ }
+ }
+
+ // Write the cache information
+ if (cacheFile.exists()) {
+ FileWriter writer = new FileWriter(cacheFile);
+ BufferedWriter bufferedWriter = new BufferedWriter(writer);
+
+ long expirationTime = System.currentTimeMillis();
+
+ // Current time + 1 day
+ expirationTime += 24 * 60 * 60 * 1000;
+
+ // Write the one line.
+ bufferedWriter.write(expirationTime + System.lineSeparator());
+ bufferedWriter.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns true if the value inside the cache file is either missing
+ *
+ * @param name the name of the file to check the cache from.
+ * @return true if the cached file should have expired.
+ */
+ public boolean isCacheExpired(String name) {
+ if (name == null) {
+ return true;
+ }
+
+ File fileCache = getCacheDirForName(name);
+
+ if (!fileCache.exists()) {
+ return true;
+ }
+
+ File cacheLock = new File(fileCache, fileCache.getName() + ".lock");
+
+ if (!cacheLock.exists()) {
+ return true;
+ }
+
+ try {
+ FileReader fileReader = new FileReader(cacheLock);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ // First line contains the expiration time.
+ String line = bufferedReader.readLine();
+
+ bufferedReader.close();
+
+ long time = Long.valueOf(line);
+
+ return System.currentTimeMillis() > time;
+ } catch (IOException ex) {
+ System.err.println("Unable to read cache file for " + name);
+
+ ex.printStackTrace();
+
+ return true;
+ } catch (NumberFormatException ex) {
+ System.err.println("Cache file had an invalid number");
+
+ ex.printStackTrace();
+
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if the cache file for a name exists.
+ *
+ * @param name the name to check for a cache file.
+ * @return true if the cache file exists
+ */
+ public boolean doesCacheExist(String name) {
+ File cacheDirectory = getCacheDirForName(name);
+
+ if (!cacheDirectory.exists()) {
+ return false;
+ }
+
+ File dataFile = new File(cacheDirectory, cacheDirectory.getName() + ".png");
+ File cacheFile = new File(cacheDirectory, cacheDirectory.getName() + ".lock");
+
+ return dataFile.exists() && cacheFile.exists();
+ }
+
+ /**
+ * Generates a unique ID from a string which can be used as a cache name.
+ *
+ * @param name the name to retrieve a cache name from
+ *
+ * @return a unique ID for the entered name which corresponds to the name's cache directory.
+ */
+ private String getCacheName(String name) {
+ name = name.toLowerCase();
+
+ // If the cache file exists in memory, just return the name of it.
+ if (this.cachedValues.containsKey(name)) {
+ return this.cachedValues.get(name);
+ }
+
+ UUID id = UUID.nameUUIDFromBytes(name.getBytes());
+
+ // Take up to the first 5 characters of the name
+ String subStrName = name.substring(0, Math.min(5, name.length()));
+
+ // Split the UUID into its four segments 0-1-2-3
+ String[] uuidSplit = id.toString().split("-");
+
+ // Take the first and second component of the UUID.
+ String idFirstComponent = uuidSplit[0] + uuidSplit[1];
+
+ // Creates the final name of the cache file.
+ String finalCacheName = subStrName + "_" + idFirstComponent;
+
+ // Cache the name so this code doesn't run on it again.
+ this.cachedValues.put(name, finalCacheName);
+
+ return finalCacheName;
+ }
+
+ /**
+ * Retrieves the cache directory from a name/input.
+ *
+ * @param nameOfFile the input which will be cached
+ * @return a directory which corresponds to a names cache file.
+ */
+ private File getCacheDirForName(String nameOfFile) {
+ String cacheName = getCacheName(nameOfFile);
+
+ return new File(this.cacheDirectory, cacheName);
+ }
+
+ /**
+ * Attempts to generate a cache directory.
+ *
+ * @return true if the cache directory already existed.
+ */
+ private boolean genCacheDirectory() {
+ boolean existed = true;
+
+ if (!this.cacheDirectory.getParentFile().exists()) {
+ existed = false;
+
+ if (this.cacheDirectory.getParentFile().mkdirs()) {
+ System.out.println("Suggested mod directory created.");
+ } else {
+ System.err.println("Unable to create the mod directory");
+
+ return false;
+ }
+ }
+
+ if (!this.cacheDirectory.exists()) {
+ existed = false;
+
+ if (this.cacheDirectory.mkdir()) {
+ System.out.println("Cache directory created.");
+ } else {
+ System.err.println("Unable to create cache directory.");
+
+ return false;
+ }
+ }
+
+ return existed;
+ }
+}
diff --git a/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/ModCommand.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/ModCommand.java
new file mode 100644
index 0000000..d0cf962
--- /dev/null
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/mod/ModCommand.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 boomboompower
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package me.do_you_like.mods.skinchanger.utils.mod;
+
+import java.util.List;
+import me.do_you_like.mods.skinchanger.SkinChangerMod;
+import me.do_you_like.mods.skinchanger.utils.backend.ThreadFactory;
+import me.do_you_like.mods.skinchanger.utils.game.ChatColor;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.ChatComponentText;
+
+/**
+ * An enhanced CommandBase implementation which provides a few extra features.
+ *
+ * @since SkinChanger v3.0
+ * @author boomboompower
+ */
+public abstract class ModCommand extends CommandBase {
+
+ // Async thread handler.
+ private final ThreadFactory threadFactory = new ThreadFactory("ModCommand");
+
+ protected SkinChangerMod mod;
+
+ public ModCommand(SkinChangerMod skinChangerMod) {
+ this.mod = skinChangerMod;
+ }
+
+ @Override
+ public String getCommandUsage(ICommandSender sender) {
+ return ChatColor.RED + "Usage: /" + getCommandName();
+ }
+
+ @Override
+ public final List getCommandAliases() {
+ return getAliases();
+ }
+
+ @Override
+ public final void processCommand(ICommandSender sender, String[] args) {
+ try {
+ if (shouldMultithreadCommand(args)) {
+ this.threadFactory.runAsync(() -> onCommand(sender, args));
+ } else {
+ onCommand(sender, args);
+ }
+ } catch (Exception ex) {
+ sendMessage(ChatColor.RED + "An error occurred whilst running this command.");
+
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * A safer way to execute a command. Errors which occur here will not cause the game to crash.
+ *
+ * @param sender the sender of the command
+ * @param args the arguments of the command
+ */
+ public abstract void onCommand(ICommandSender sender, String[] args);
+
+ public abstract List getAliases();
+
+ @Override
+ public boolean canCommandSenderUseCommand(ICommandSender sender) {
+ return true;
+ }
+
+ /**
+ * Return true if the command should be run on its own thread.
+ *
+ * @param args the arguments of the command
+ * @return true if the command should be async
+ */
+ protected boolean shouldMultithreadCommand(String[] args) {
+ return false;
+ }
+
+ /**
+ * Sends a raw message to the client.
+ *
+ * @param message the message to send
+ */
+ protected void sendMessage(String message) {
+ Minecraft.getMinecraft().thePlayer.addChatComponentMessage(new ChatComponentText(message));
+ }
+}
diff --git a/src/main/java/me/boomboompower/skinchanger/utils/WebsiteUtils.java b/src/main/java/me/do_you_like/mods/skinchanger/utils/updater/WebsiteUtils.java
similarity index 98%
rename from src/main/java/me/boomboompower/skinchanger/utils/WebsiteUtils.java
rename to src/main/java/me/do_you_like/mods/skinchanger/utils/updater/WebsiteUtils.java
index 520ebf5..1fd4e88 100644
--- a/src/main/java/me/boomboompower/skinchanger/utils/WebsiteUtils.java
+++ b/src/main/java/me/do_you_like/mods/skinchanger/utils/updater/WebsiteUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 boomboompower
+ * Copyright (C) 2020 boomboompower
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,13 +15,12 @@
* along with this program. If not, see .
*/
-package me.boomboompower.skinchanger.utils;
+package me.do_you_like.mods.skinchanger.utils.updater;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedList;
@@ -31,9 +30,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-
-import me.boomboompower.skinchanger.SkinChangerMod;
-
+import me.boomboompower.skinchanger.SkinChangerModOld;
+import me.boomboompower.skinchanger.utils.ChatColor;
import net.minecraft.client.Minecraft;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
@@ -43,7 +41,6 @@
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
-
import org.apache.commons.io.IOUtils;
/**
@@ -159,7 +156,7 @@ public void begin() {
this.showUpdateHeader = object.get("updateheader").getAsBoolean();
}
- int currentVersion = formatVersion(SkinChangerMod.VERSION);
+ int currentVersion = formatVersion(SkinChangerModOld.VERSION);
int latestVersion = object.has("latest-version") ? formatVersion(object.get("latest-version").getAsString()) : -1;
if (currentVersion < latestVersion && latestVersion > 0) {
@@ -339,7 +336,7 @@ private int formatVersion(String input) {
@SubscribeEvent(priority = EventPriority.LOW)
public void onJoin(FMLNetworkEvent.ClientConnectedToServerEvent event) {
- WebsiteUtils utils = SkinChangerMod.getInstance().getWebsiteUtils();
+ WebsiteUtils utils = this;
if (utils.isDisabled()) return;
diff --git a/src/main/resources/mixins.skinchanger.json b/src/main/resources/mixins.skinchanger.json
index 12fbe7a..769ea22 100644
--- a/src/main/resources/mixins.skinchanger.json
+++ b/src/main/resources/mixins.skinchanger.json
@@ -1,5 +1,5 @@
{
- "package": "me.boomboompower.skinchanger.mixins",
+ "package": "me.do_you_like.mods.skinchanger.methods.impl.mixins",
"refmap": "mixins.skinchanger.refmap.json",
"compatibilityLevel": "JAVA_8",
"verbose": true,