diff --git a/gradle.properties b/gradle.properties index 60d60e9..c91a0aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx1G loader_version=0.14.9 # Mod Properties - mod_version = 3.0.15 + mod_version = 3.0.13 maven_group = ca.rttv archives_base_name = chatcalc diff --git a/src/main/java/ca/rttv/chatcalc/Config.java b/src/main/java/ca/rttv/chatcalc/Config.java index de8a49e..077554a 100644 --- a/src/main/java/ca/rttv/chatcalc/Config.java +++ b/src/main/java/ca/rttv/chatcalc/Config.java @@ -2,6 +2,7 @@ import ca.rttv.chatcalc.tokens.NumberToken; import ca.rttv.chatcalc.tokens.Token; +import com.google.common.collect.ImmutableMap; import com.google.gson.*; import net.minecraft.client.MinecraftClient; import net.minecraft.util.Pair; @@ -16,8 +17,17 @@ public class Config { public static final Gson GSON; public static final File CONFIG_FILE; public static final Map, String>> FUNCTIONS; + public static final ImmutableMap DEFAULTS; static { + DEFAULTS = ImmutableMap.builder() + .put("decimal_format", "#,##0.##") + .put("radians", "false") + .put("debug_tokens", "false") + .put("log_exceptions", "false") + .put("copy_type", "none") + .put("calculate_last", "true") + .build(); CONFIG_FILE = new File(".", "config/chatcalc.json"); GSON = new GsonBuilder().setPrettyPrinting().create(); JSON = new JsonObject(); @@ -27,38 +37,35 @@ public class Config { //noinspection ResultOfMethodCallIgnored CONFIG_FILE.createNewFile(); FileWriter writer = new FileWriter(CONFIG_FILE); - writer.write(""" - { - "decimal_format": "#,##0.##", - "radians": "false", - "debug_tokens": "false", - "euler": "false", - "log_exceptions": "false", - "copy_type": "none" - } - """); + writer.write("{\n"); + for (Map.Entry element : DEFAULTS.entrySet()) { + writer.write(String.format(" \"%s\": \"%s\",\n", element.getKey(), element.getValue())); + } + writer.write(" \"functions\": []\n"); + writer.write("}"); writer.close(); } catch (IOException ignored) {} } + FUNCTIONS = new HashMap<>(); if (CONFIG_FILE.exists() && CONFIG_FILE.isFile() && CONFIG_FILE.canRead()) { readJson(); } - FUNCTIONS = new HashMap<>(); - if (JSON.has("functions")) { - JSON.getAsJsonArray("functions").forEach(e -> FUNCTIONS.put(e.getAsString().split("\\(")[0], new Pair<>(MathEngine.tokenize(e.getAsString().split("=")[1]), e.getAsString().split("[()]")[1]))); - } + } + + public static boolean calculateLast() { + return Boolean.parseBoolean(JSON.get("calculate_last").getAsString()); } public static DecimalFormat getDecimalFormat() { - return JSON.has("decimal_format") ? new DecimalFormat(JSON.get("decimal_format").getAsString()) : new DecimalFormat("#,##0.##"); + return new DecimalFormat(JSON.get("decimal_format").getAsString()); } public static double convertIfRadians(double value) { - return JSON.has("radians") && Boolean.parseBoolean(JSON.get("radians").getAsString()) ? Math.toRadians(value) : value; + return Boolean.parseBoolean(JSON.get("radians").getAsString()) ? Math.toRadians(value) : value; } public static boolean logExceptions() { - return JSON.has("log_exceptions") && Boolean.parseBoolean(JSON.get("log_exceptions").getAsString()); + return Boolean.parseBoolean(JSON.get("log_exceptions").getAsString()); } public static void refreshJson() { @@ -66,29 +73,33 @@ public static void refreshJson() { FileWriter writer = new FileWriter(CONFIG_FILE); JSON.add("functions", FUNCTIONS.entrySet().stream().map(x -> x.getKey() + "(" + x.getValue().getRight() + ")=" + x.getValue().getLeft().stream().map(Object::toString).collect(Collectors.joining())).collect(JsonArray::new, JsonArray::add, JsonArray::addAll)); writer.write(GSON.toJson(JSON)); + JSON.remove("functions"); writer.close(); } catch (Exception ignored) { } } public static void readJson() { - try { - BufferedReader reader = new BufferedReader(new FileReader(CONFIG_FILE)); - JSON.entrySet().forEach(entry -> JSON.remove(entry.getKey())); - JsonParser.parseString(reader.lines().collect(Collectors.joining("\n"))).getAsJsonObject().entrySet().forEach(entry -> JSON.add(entry.getKey(), entry.getValue())); - reader.close(); + try (BufferedReader reader = new BufferedReader(new FileReader(CONFIG_FILE))) { + JsonObject tempJson; + try { + tempJson = JsonParser.parseString(reader.lines().collect(Collectors.joining("\n"))).getAsJsonObject(); + } catch (Exception ignored) { + tempJson = new JsonObject(); + } + JsonObject json = tempJson; // annoying lambda requirement + DEFAULTS.forEach((key, defaultValue) -> JSON.add(key, json.get(key) instanceof JsonPrimitive primitive && primitive.isString() ? primitive : new JsonPrimitive(defaultValue))); + if (json.get("functions") instanceof JsonArray array) { + array.forEach(e -> FUNCTIONS.put(e.getAsString().split("\\(")[0], new Pair<>(MathEngine.tokenize(e.getAsString().split("=")[1]), e.getAsString().split("[()]")[1]))); + } } catch (Exception ignored) { } } public static boolean debugTokens() { - return JSON.has("debug_tokens") && Boolean.parseBoolean(JSON.get("debug_tokens").getAsString()); - } - - public static boolean euler() { - return JSON.has("euler") && Boolean.parseBoolean(JSON.get("euler").getAsString()); + return Boolean.parseBoolean(JSON.get("debug_tokens").getAsString()); } public static void saveToChatHud(String input) { - if (JSON.has("copy_type") && JSON.get("copy_type").getAsString().equalsIgnoreCase("chat_history")) { + if (JSON.get("copy_type").getAsString().equalsIgnoreCase("chat_history")) { final MinecraftClient client = MinecraftClient.getInstance(); client.inGameHud.getChatHud().addToMessageHistory(input); } @@ -105,7 +116,7 @@ public static double func(String name, double value) { } public static void saveToClipboard(String input) { - if (JSON.has("copy_type") && JSON.get("copy_type").getAsString().equalsIgnoreCase("clipboard")) { + if (JSON.get("copy_type").getAsString().equalsIgnoreCase("clipboard")) { final MinecraftClient client = MinecraftClient.getInstance(); client.keyboard.setClipboard(input); } diff --git a/src/main/java/ca/rttv/chatcalc/MathEngine.java b/src/main/java/ca/rttv/chatcalc/MathEngine.java index 46abdf1..ada4fd5 100644 --- a/src/main/java/ca/rttv/chatcalc/MathEngine.java +++ b/src/main/java/ca/rttv/chatcalc/MathEngine.java @@ -42,15 +42,12 @@ public static double eval(String input) { @Contract(value = "!null->!null", pure = true) public static List tokenize(String input) { - MinecraftClient client = MinecraftClient.getInstance(); input = input.toLowerCase() .replace("pi", "3.141592653589793") .replace("tau", "6.283185307179586") .replace("**", "^") - .replaceAll(",", ""); - if (Config.euler()) { - input = input.replaceAll("(?!c)e(?!il)", "2.718281828459045"); - } + .replaceAll(",", "") + .replaceAll("", "2.718281828459045"); List tokens = new ArrayList<>(input.length() >> 1); // just a guess { Optional> currentType = Optional.empty(); @@ -147,6 +144,7 @@ public static List tokenize(String input) { @Contract(value = "_,_,_->_", mutates = "param1") public static void simplify(List tokens, boolean abs, Optional> variable) { + final MinecraftClient client = MinecraftClient.getInstance(); for (int i = 0; i < tokens.size(); i++) { Token token = tokens.get(i); if (token instanceof BracketToken bracketToken) { @@ -172,11 +170,11 @@ public static void simplify(List tokens, boolean abs, Optional MinecraftClient.getInstance().player.getX(); - case 'y' -> MinecraftClient.getInstance().player.getY(); - case 'z' -> MinecraftClient.getInstance().player.getZ(); + case 'x' -> client.player.getX(); + case 'y' -> client.player.getY(); + case 'z' -> client.player.getZ(); default -> throw new NullPointerException(); })); } else { diff --git a/src/main/java/ca/rttv/chatcalc/duck/ChatInputSuggesterDuck.java b/src/main/java/ca/rttv/chatcalc/duck/ChatInputSuggesterDuck.java new file mode 100644 index 0000000..5598f7f --- /dev/null +++ b/src/main/java/ca/rttv/chatcalc/duck/ChatInputSuggesterDuck.java @@ -0,0 +1,9 @@ +package ca.rttv.chatcalc.duck; + +import com.mojang.brigadier.suggestion.Suggestions; + +import java.util.concurrent.CompletableFuture; + +public interface ChatInputSuggesterDuck { + CompletableFuture chatcalc$pendingSuggestions(); +} diff --git a/src/main/java/ca/rttv/chatcalc/mixin/ChatInputSuggesterMixin.java b/src/main/java/ca/rttv/chatcalc/mixin/ChatInputSuggesterMixin.java new file mode 100644 index 0000000..754820e --- /dev/null +++ b/src/main/java/ca/rttv/chatcalc/mixin/ChatInputSuggesterMixin.java @@ -0,0 +1,20 @@ +package ca.rttv.chatcalc.mixin; + +import ca.rttv.chatcalc.duck.ChatInputSuggesterDuck; +import com.mojang.brigadier.suggestion.Suggestions; +import net.minecraft.client.gui.screen.ChatInputSuggestor; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.concurrent.CompletableFuture; + +@Mixin(ChatInputSuggestor.class) +abstract class ChatInputSuggesterMixin implements ChatInputSuggesterDuck { + @Shadow @Nullable private CompletableFuture pendingSuggestions; + + @Override + public CompletableFuture chatcalc$pendingSuggestions() { + return this.pendingSuggestions; + } +} diff --git a/src/main/java/ca/rttv/chatcalc/mixin/ChatScreenMixin.java b/src/main/java/ca/rttv/chatcalc/mixin/ChatScreenMixin.java index cb3759f..fd0e38c 100644 --- a/src/main/java/ca/rttv/chatcalc/mixin/ChatScreenMixin.java +++ b/src/main/java/ca/rttv/chatcalc/mixin/ChatScreenMixin.java @@ -1,6 +1,10 @@ package ca.rttv.chatcalc.mixin; import ca.rttv.chatcalc.ChatCalc; +import ca.rttv.chatcalc.Config; +import ca.rttv.chatcalc.duck.ChatInputSuggesterDuck; +import com.mojang.brigadier.suggestion.Suggestions; +import net.minecraft.client.gui.screen.ChatInputSuggestor; import net.minecraft.client.gui.screen.ChatScreen; import net.minecraft.client.gui.widget.TextFieldWidget; import org.spongepowered.asm.mixin.Mixin; @@ -9,15 +13,24 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.concurrent.CompletableFuture; + @Mixin(ChatScreen.class) abstract class ChatScreenMixin { @Shadow protected TextFieldWidget chatField; + @Shadow + ChatInputSuggestor chatInputSuggestor; + @Inject(at = @At("HEAD"), method = "keyPressed(III)Z", cancellable = true) private void keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { - if (keyCode == 258 && ChatCalc.tryParse(chatField)) { - cir.setReturnValue(true); + CompletableFuture suggestions = ((ChatInputSuggesterDuck) this.chatInputSuggestor).chatcalc$pendingSuggestions(); + // I have never dealt with CompletableFuture before, so I don't know if there's a method to check if everything went well + if (!Config.calculateLast() || (suggestions != null && suggestions.isDone() && !suggestions.isCompletedExceptionally() && suggestions.getNow(null).isEmpty())) { + if (keyCode == 258 && ChatCalc.tryParse(chatField)) { + cir.setReturnValue(true); + } } } } diff --git a/src/main/resources/assets/chatcalc/lang/en_us.json b/src/main/resources/assets/chatcalc/lang/en_us.json index 550e4bf..66c074e 100644 --- a/src/main/resources/assets/chatcalc/lang/en_us.json +++ b/src/main/resources/assets/chatcalc/lang/en_us.json @@ -2,7 +2,7 @@ "chatcalc.decimal_format.description": "The decimal format used by the mod to display numbers; ex: #,##0.## has commas, numbers smaller than 0 have a leading 0 (.3 into 0.3), and stores two decimal places.\nMore information can be found here: https://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html\nUsing an invalid format *will* crash the game", "chatcalc.radians.description": "A toggle to convert numbers from degrees to radians before calculating in trig equations such as sin(), cos(), and tan()", "chatcalc.debug_tokens.description": "A debug option to display all of the parsed tokens in chat and console before they get evaluated", - "chatcalc.e.description": "Toggles the parsing of the e constant (euler's number), aka 2.718281828459045", "chatcalc.log_exceptions.description": "Logs exceptions to your minecraft logs. The reason this is a toggle is because when pressing tab for non chatcalc purposes, it throws an error", - "chatcalc.copy_type.description": "An option either copy to your clipboard (value: clipboard) or add it to your chat history (value: chat_history) (the thing when pressing the up arrow and down arrow), or to not save to anywhere (none)" + "chatcalc.copy_type.description": "An option either copy to your clipboard (value: clipboard) or add it to your chat history (value: chat_history) (the thing when pressing the up arrow and down arrow), or to not save to anywhere (none)", + "chatcalc.calculate_last.description": "When tabbing in usernames or subcommands, chatcalc could take precedence over those actions. Which is particularly annoying for usernames that start with x, y, z, etc. (default: true)" } \ No newline at end of file diff --git a/src/main/resources/chatcalc.mixins.json b/src/main/resources/chatcalc.mixins.json index 3b1b940..e1fc764 100644 --- a/src/main/resources/chatcalc.mixins.json +++ b/src/main/resources/chatcalc.mixins.json @@ -5,6 +5,7 @@ "compatibilityLevel": "JAVA_17", "mixins": [], "client": [ + "ChatInputSuggesterMixin", "ChatScreenMixin" ], "injectors": {