Skip to content
This repository has been archived by the owner on Jun 30, 2024. It is now read-only.

Commit

Permalink
- refactored config system so it doesn't break when i add new feature…
Browse files Browse the repository at this point in the history
…s now

- fixed issue #6 by making a new option (calculate_last, on by default) that puts calculations after tabbing in entries like commands and usernames (well I haven't tested usernames but it shouldn't go first)
- because of the fix, ive removed the euler config option, existing always since the issue beforehand is that you can commonly type in 'e' and it gives you a number instead of what you want
- the only things i see left are to add new features, so if you have anything, feel free to ask
  • Loading branch information
RealRTTV committed May 14, 2023
1 parent 6783e13 commit 32cc6bb
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 43 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
69 changes: 40 additions & 29 deletions src/main/java/ca/rttv/chatcalc/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,8 +17,17 @@ public class Config {
public static final Gson GSON;
public static final File CONFIG_FILE;
public static final Map<String, Pair<List<Token>, String>> FUNCTIONS;
public static final ImmutableMap<String, String> DEFAULTS;

static {
DEFAULTS = ImmutableMap.<String, String>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();
Expand All @@ -27,68 +37,69 @@ 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<String, String> 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() {
try {
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);
}
Expand All @@ -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);
}
Expand Down
16 changes: 7 additions & 9 deletions src/main/java/ca/rttv/chatcalc/MathEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,12 @@ public static double eval(String input) {

@Contract(value = "!null->!null", pure = true)
public static List<Token> 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<Token> tokens = new ArrayList<>(input.length() >> 1); // just a guess
{
Optional<Class<? extends Token>> currentType = Optional.empty();
Expand Down Expand Up @@ -147,6 +144,7 @@ public static List<Token> tokenize(String input) {

@Contract(value = "_,_,_->_", mutates = "param1")
public static void simplify(List<Token> tokens, boolean abs, Optional<Pair<String, Double>> variable) {
final MinecraftClient client = MinecraftClient.getInstance();
for (int i = 0; i < tokens.size(); i++) {
Token token = tokens.get(i);
if (token instanceof BracketToken bracketToken) {
Expand All @@ -172,11 +170,11 @@ public static void simplify(List<Token> tokens, boolean abs, Optional<Pair<Strin
} else if (token instanceof FunctionToken functionToken) { // function
if (variable.isPresent() && variable.get().getLeft().equals(functionToken.func)) { // since 'a' will be considered a function, I have to detect if it is, then convert it into a constant
tokens.set(i, new NumberToken(variable.get().getRight()));
} else if (functionToken.func.length() == 1 && isPos(functionToken.func.charAt(0))) {
} else if (functionToken.func.length() == 1 && isPos(functionToken.func.charAt(0)) && client.player != null) {
tokens.set(i, new NumberToken(switch (functionToken.func.charAt(0)) {
case 'x' -> 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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ca.rttv.chatcalc.duck;

import com.mojang.brigadier.suggestion.Suggestions;

import java.util.concurrent.CompletableFuture;

public interface ChatInputSuggesterDuck {
CompletableFuture<Suggestions> chatcalc$pendingSuggestions();
}
20 changes: 20 additions & 0 deletions src/main/java/ca/rttv/chatcalc/mixin/ChatInputSuggesterMixin.java
Original file line number Diff line number Diff line change
@@ -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<Suggestions> pendingSuggestions;

@Override
public CompletableFuture<Suggestions> chatcalc$pendingSuggestions() {
return this.pendingSuggestions;
}
}
17 changes: 15 additions & 2 deletions src/main/java/ca/rttv/chatcalc/mixin/ChatScreenMixin.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Boolean> cir) {
if (keyCode == 258 && ChatCalc.tryParse(chatField)) {
cir.setReturnValue(true);
CompletableFuture<Suggestions> 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);
}
}
}
}
4 changes: 2 additions & 2 deletions src/main/resources/assets/chatcalc/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
}
1 change: 1 addition & 0 deletions src/main/resources/chatcalc.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"compatibilityLevel": "JAVA_17",
"mixins": [],
"client": [
"ChatInputSuggesterMixin",
"ChatScreenMixin"
],
"injectors": {
Expand Down

0 comments on commit 32cc6bb

Please sign in to comment.