diff --git a/.idea/artifacts/TerrarumBuild.xml b/.idea/artifacts/TerrarumBuild.xml
index 793cc2b9f..3f1077feb 100644
--- a/.idea/artifacts/TerrarumBuild.xml
+++ b/.idea/artifacts/TerrarumBuild.xml
@@ -13,6 +13,10 @@
+
+
+
+
@@ -72,21 +76,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Principii.xml b/.idea/runConfigurations/Principii.xml
new file mode 100644
index 000000000..f4fc9b79a
--- /dev/null
+++ b/.idea/runConfigurations/Principii.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/locales/en/terrarum.json b/assets/locales/en/terrarum.json
index 08f2dd26a..33bceff7a 100644
--- a/assets/locales/en/terrarum.json
+++ b/assets/locales/en/terrarum.json
@@ -27,5 +27,8 @@
"MENU_LABEL_IME_TOGGLE": "Toggle IME",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Cliboard",
"MENU_OPTIONS_PERFORMANCE": "Performance",
- "MENU_LABEL_DELETE": "Delete"
+ "MENU_LABEL_DELETE": "Delete",
+ "MENU_OPTIONS_JVM_HEAP_MAX": "Max JVM Heap Memory",
+ "MENU_OPTIONS_AUTOSAVE": "Autosave",
+ "CONTEXT_TIME_MINUTE_PLURAL": "Minutes"
}
\ No newline at end of file
diff --git a/buildapp/build_app_linux_arm.sh b/buildapp/build_app_linux_arm.sh
index 5976f423c..55c4aee96 100755
--- a/buildapp/build_app_linux_arm.sh
+++ b/buildapp/build_app_linux_arm.sh
@@ -28,7 +28,8 @@ cp -r "../out/$RUNTIME" $DESTDIR/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
-cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
+mkdir $DESTDIR/out
+cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Pack everything to AppImage
ARCH=arm_aarch64 "./$APPIMAGETOOL" $DESTDIR "out/$DESTDIR.AppImage" || { echo 'Building AppImage failed' >&2; exit 1; }
diff --git a/buildapp/build_app_linux_x86.sh b/buildapp/build_app_linux_x86.sh
index 69a498fe2..d824d4d7a 100755
--- a/buildapp/build_app_linux_x86.sh
+++ b/buildapp/build_app_linux_x86.sh
@@ -28,7 +28,8 @@ cp -r "../out/$RUNTIME" $DESTDIR/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
-cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
+mkdir $DESTDIR/out
+cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Pack everything to AppImage
"./$APPIMAGETOOL" $DESTDIR "out/$DESTDIR.AppImage" || { echo 'Building AppImage failed' >&2; exit 1; }
diff --git a/buildapp/build_app_mac_arm.sh b/buildapp/build_app_mac_arm.sh
index 5c2c55105..08167f837 100755
--- a/buildapp/build_app_mac_arm.sh
+++ b/buildapp/build_app_mac_arm.sh
@@ -30,6 +30,7 @@ cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
-cp -r "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/assets/
+mkdir $DESTDIR/Contents/MacOS/out
+cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
echo "Build successful: $DESTDIR"
diff --git a/buildapp/build_app_mac_x86.sh b/buildapp/build_app_mac_x86.sh
index a170ace0c..33c2e60c5 100755
--- a/buildapp/build_app_mac_x86.sh
+++ b/buildapp/build_app_mac_x86.sh
@@ -30,6 +30,7 @@ cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
-cp -r "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/assets/
+mkdir $DESTDIR/Contents/MacOS/out
+cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
echo "Build successful: $DESTDIR"
diff --git a/buildapp/build_app_windows_x86.sh b/buildapp/build_app_windows_x86.sh
index d151f0946..4e149501b 100755
--- a/buildapp/build_app_windows_x86.sh
+++ b/buildapp/build_app_windows_x86.sh
@@ -29,7 +29,8 @@ cp -r "../out/$RUNTIME" $DESTDIR/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
-cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
+mkdir $DESTDIR/out
+cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Temporary solution: zip everything
zip -r -9 -l "out/TerrarumWindows.x86.zip" $DESTDIR
diff --git a/buildapp/terrarumlinux_arm/AppRun b/buildapp/terrarumlinux_arm/AppRun
index 69f74acdd..c0f260628 100755
--- a/buildapp/terrarumlinux_arm/AppRun
+++ b/buildapp/terrarumlinux_arm/AppRun
@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
-./runtime-linux-arm/bin/java -Xms1G -Xmx6G -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./assets/TerrarumBuild.jar
+./runtime-linux-arm/bin/java -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./out/TerrarumBuild.jar
diff --git a/buildapp/terrarumlinux_x86/AppRun b/buildapp/terrarumlinux_x86/AppRun
index 353f3ab90..78ce88112 100755
--- a/buildapp/terrarumlinux_x86/AppRun
+++ b/buildapp/terrarumlinux_x86/AppRun
@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
-./runtime-linux-x86/bin/java -Xms1G -Xmx6G -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./assets/TerrarumBuild.jar
+./runtime-linux-x86/bin/java -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./out/TerrarumBuild.jar
diff --git a/buildapp/terrarummac_arm/Terrarum.sh b/buildapp/terrarummac_arm/Terrarum.sh
index 8f99418c5..6b067c834 100755
--- a/buildapp/terrarummac_arm/Terrarum.sh
+++ b/buildapp/terrarummac_arm/Terrarum.sh
@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
-./runtime-osx-arm/bin/java -XstartOnFirstThread -Xms1G -Xmx6G -jar ./assets/TerrarumBuild.jar
+./runtime-osx-arm/bin/java -XstartOnFirstThread -jar ./out/TerrarumBuild.jar
diff --git a/buildapp/terrarummac_x86/Terrarum.sh b/buildapp/terrarummac_x86/Terrarum.sh
index 5bcf6f94f..2d4341419 100755
--- a/buildapp/terrarummac_x86/Terrarum.sh
+++ b/buildapp/terrarummac_x86/Terrarum.sh
@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
-./runtime-osx-x86/bin/java -XstartOnFirstThread -Xms1G -Xmx6G -jar ./assets/TerrarumBuild.jar
+./runtime-osx-x86/bin/java -XstartOnFirstThread -jar ./out/TerrarumBuild.jar
diff --git a/buildapp/terrarumwindows_x86/Terrarum.c b/buildapp/terrarumwindows_x86/Terrarum.c
index a01642dee..61171ddff 100644
--- a/buildapp/terrarumwindows_x86/Terrarum.c
+++ b/buildapp/terrarumwindows_x86/Terrarum.c
@@ -2,5 +2,5 @@
#include
int main() {
- return system(".\\runtime-windows-x86\\bin\\java -Xms1G -Xmx6G -jar .\\assets\\TerrarumBuild.jar");
+ return system(".\\runtime-windows-x86\\bin\\java -jar .\\out\\TerrarumBuild.jar");
}
\ No newline at end of file
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
index d6603425b..92250febb 100644
--- a/src/META-INF/MANIFEST.MF
+++ b/src/META-INF/MANIFEST.MF
@@ -1,3 +1,3 @@
Manifest-Version: 1.0
-Main-Class: net.torvald.terrarum.App
+Main-Class: net.torvald.terrarum.Principii
diff --git a/src/net/torvald/terrarum/DefaultConfig.kt b/src/net/torvald/terrarum/DefaultConfig.kt
index d29ecc9d2..e3a4de619 100644
--- a/src/net/torvald/terrarum/DefaultConfig.kt
+++ b/src/net/torvald/terrarum/DefaultConfig.kt
@@ -10,6 +10,7 @@ import com.badlogic.gdx.Input
object DefaultConfig {
val hashMap = hashMapOf(
+ "jvm_xmx" to 8,
"displayfps" to 0, // 0: no limit, non-zero: limit
"displayfpsidle" to 0, // 0: no limit, non-zero: limit
"displaycolourdepth" to 8,
@@ -21,7 +22,7 @@ object DefaultConfig {
"language" to App.getSysLang(),
"notificationshowuptime" to 4096, // 4s
"selecteditemnameshowuptime" to 4096, // 4s
- "autosaveinterval" to 262144, // 4m22s
+ "autosaveinterval" to 300000, // 5s
"multithread" to true,
"showhealthmessageonstartup" to true,
diff --git a/src/net/torvald/terrarum/ModMgr.kt b/src/net/torvald/terrarum/ModMgr.kt
index 5fca49d55..21ec8ca0b 100644
--- a/src/net/torvald/terrarum/ModMgr.kt
+++ b/src/net/torvald/terrarum/ModMgr.kt
@@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.JsonValue
import net.torvald.terrarum.App.*
+import net.torvald.terrarum.App.setToGameConfig
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.gameitems.GameItem
diff --git a/src/net/torvald/terrarum/Principii.java b/src/net/torvald/terrarum/Principii.java
new file mode 100644
index 000000000..07baccd0e
--- /dev/null
+++ b/src/net/torvald/terrarum/Principii.java
@@ -0,0 +1,370 @@
+package net.torvald.terrarum;
+
+import com.badlogic.gdx.utils.JsonValue;
+import net.torvald.terrarum.serialise.WriteConfig;
+import net.torvald.terrarum.utils.JsonFetcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by minjaesong on 2023-06-22.
+ */
+public class Principii {
+
+ public static KVHashMap gameConfig = new KVHashMap();
+
+ public static String OSName = System.getProperty("os.name");
+
+ public static String operationSystem;
+ /** %appdata%/Terrarum, without trailing slash */
+ public static String defaultDir;
+ /** defaultDir + "/config.json" */
+ public static String configDir;
+
+
+ public static void getDefaultDirRoot() {
+ String OS = OSName.toUpperCase();
+ if (OS.contains("WIN")) {
+ operationSystem = "WINDOWS";
+ defaultDir = System.getenv("APPDATA") + "/Terrarum";
+ }
+ else if (OS.contains("OS X") || OS.contains("MACOS")) { // OpenJDK for mac will still report "Mac OS X" with version number "10.16", even on Big Sur and beyond
+ operationSystem = "OSX";
+ defaultDir = System.getProperty("user.home") + "/Library/Application Support/Terrarum";
+ }
+ else if (OS.contains("NUX") || OS.contains("NIX") || OS.contains("BSD")) {
+ operationSystem = "LINUX";
+ defaultDir = System.getProperty("user.home") + "/.Terrarum";
+ }
+ else if (OS.contains("SUNOS")) {
+ operationSystem = "SOLARIS";
+ defaultDir = System.getProperty("user.home") + "/.Terrarum";
+ }
+ else {
+ operationSystem = "UNKNOWN";
+ defaultDir = System.getProperty("user.home") + "/.Terrarum";
+ }
+ }
+
+
+ public static void main(String[] args) {
+
+ boolean devMode = false;
+
+ // if -ea flag is set, turn on all the debug prints
+ try {
+ assert false;
+ }
+ catch (AssertionError e) {
+ devMode = true;
+ }
+
+ String runtimeDir = null;
+ String extracmd = devMode ? " -ea" : "";
+ String OS = OSName.toUpperCase();
+ String CPUARCH = System.getProperty("os.arch").toUpperCase();
+ if (OS.contains("WIN")) {
+ if (CPUARCH.equals("AMD64"))
+ runtimeDir = "runtime-windows-x86";
+ }
+ else if (OS.contains("OS X") || OS.contains("MACOS")) { // OpenJDK for mac will still report "Mac OS X" with version number "10.16", even on Big Sur and beyond
+ extracmd += " -XstartOnFirstThread";
+ if (CPUARCH.equals("AMD64"))
+ runtimeDir = "runtime-osx-x86";
+ else if (CPUARCH.equals("AARCH64"))
+ runtimeDir = "runtime-osx-arm";
+ }
+ else {
+ extracmd += " -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd";
+ if (CPUARCH.equals("AMD64") || CPUARCH.equals("X86"))
+ runtimeDir = "runtime-linux-x86";
+ else if (CPUARCH.equals("AARCH64"))
+ runtimeDir = "runtime-linux-arm";
+ }
+
+ if (runtimeDir == null) {
+ // TODO show error message of incompatible processor (possibly 32-bit?)
+ System.exit(1);
+ }
+
+
+
+
+ getDefaultDirRoot();
+ configDir = defaultDir + "/config.json";
+
+ initialiseConfig();
+ readConfigJson();
+
+
+ int xmx = getConfigInt("jvm_xmx");
+
+ try {
+ Process proc = Runtime.getRuntime().exec("java"+extracmd+" -Xms1G -Xmx"+xmx+"G -cp ./out/TerrarumBuild.jar net.torvald.terrarum.App");
+
+ // TODO redirect proc's PrintStream to System.out
+ int size = 0;
+ byte[] buffer = new byte[1024];
+ while ((size = proc.getInputStream().read(buffer)) != -1) {
+ System.out.write(buffer, 0, size);
+ }
+
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ // CONFIG //
+
+
+
+ /**
+ * Return config from config set. If the config does not exist, default value will be returned.
+ * @param key
+ * *
+ * @return Config from config set or default config if it does not exist.
+ * *
+ * @throws NullPointerException if the specified config simply does not exist.
+ */
+ public static int getConfigInt(String key) {
+ Object cfg = getConfigMaster(key);
+
+ if (cfg instanceof Integer) return ((int) cfg);
+
+ double value = (double) cfg;
+
+ if (Math.abs(value % 1.0) < 0.00000001)
+ return (int) Math.round(value);
+ return ((int) cfg);
+ }
+
+
+ /**
+ * Return config from config set. If the config does not exist, default value will be returned.
+ * @param key
+ * *
+ * @return Config from config set or default config if it does not exist.
+ * *
+ * @throws NullPointerException if the specified config simply does not exist.
+ */
+ public static double getConfigDouble(String key) {
+ Object cfg = getConfigMaster(key);
+ return (cfg instanceof Integer) ? (((Integer) cfg) * 1.0) : ((double) (cfg));
+ }
+
+ /**
+ * Return config from config set. If the config does not exist, default value will be returned.
+ * @param key
+ * *
+ * @return Config from config set or default config if it does not exist.
+ * *
+ * @throws NullPointerException if the specified config simply does not exist.
+ */
+ public static String getConfigString(String key) {
+ Object cfg = getConfigMaster(key);
+ return ((String) cfg);
+ }
+
+ /**
+ * Return config from config set. If the config does not exist, default value will be returned.
+ * @param key
+ * *
+ * @return Config from config set or default config if it does not exist. If the default value is undefined, will return false.
+ */
+ public static boolean getConfigBoolean(String key) {
+ try {
+ Object cfg = getConfigMaster(key);
+ return ((boolean) cfg);
+ }
+ catch (NullPointerException keyNotFound) {
+ return false;
+ }
+ }
+
+ /*public static int[] getConfigIntArray(String key) {
+ Object cfg = getConfigMaster(key);
+ if (cfg instanceof JsonArray) {
+ JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
+ //return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt })
+ int[] intArray = new int[jsonArray.size()];
+ for (int i = 0; i < jsonArray.size(); i++) {
+ intArray[i] = jsonArray.get(i).getAsInt();
+ }
+ return intArray;
+ }
+ else
+ return ((int[]) cfg);
+ }*/
+
+ public static double[] getConfigDoubleArray(String key) {
+ Object cfg = getConfigMaster(key);
+ return ((double[]) cfg);
+ }
+
+ public static int[] getConfigIntArray(String key) {
+ double[] a = getConfigDoubleArray(key);
+ int[] r = new int[a.length];
+ for (int i = 0; i < a.length; i++) {
+ r[i] = ((int) a[i]);
+ }
+ return r;
+ }
+
+ /*public static String[] getConfigStringArray(String key) {
+ Object cfg = getConfigMaster(key);
+ if (cfg instanceof JsonArray) {
+ JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
+ //return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt })
+ String[] intArray = new String[jsonArray.size()];
+ for (int i = 0; i < jsonArray.size(); i++) {
+ intArray[i] = jsonArray.get(i).getAsString();
+ }
+ return intArray;
+ }
+ else
+ return ((String[]) cfg);
+ }*/
+
+ /**
+ * Get config from config file. If the entry does not exist, get from defaults; if the entry is not in the default, NullPointerException will be thrown
+ */
+ private static HashMap getDefaultConfig() {
+ return DefaultConfig.INSTANCE.getHashMap();
+ }
+
+ private static Object getConfigMaster(String key1) {
+ String key = key1.toLowerCase();
+
+ Object config;
+ try {
+ config = gameConfig.get(key);
+ }
+ catch (NullPointerException e) {
+ config = null;
+ }
+
+ Object defaults;
+ try {
+ defaults = getDefaultConfig().get(key);
+ }
+ catch (NullPointerException e) {
+ defaults = null;
+ }
+
+ if (config == null) {
+ if (defaults == null) {
+ throw new NullPointerException("key not found: '" + key + "'");
+ }
+ else {
+ return defaults;
+ }
+ }
+ else {
+ return config;
+ }
+ }
+
+ public static void setConfig(String key, Object value) {
+ gameConfig.set(key.toLowerCase(), value);
+ }
+
+
+ /**
+ *
+ * @return true on successful, false on failure.
+ */
+ private static Boolean readConfigJson() {
+ System.out.println("Config file: " + configDir);
+
+
+ try {
+ // read from disk and build config from it
+ JsonValue map = JsonFetcher.INSTANCE.invoke(configDir);
+
+ // make config
+ for (JsonValue entry = map.child; entry != null; entry = entry.next) {
+ setToGameConfigForced(entry, null);
+ }
+
+ return true;
+ }
+ catch (IOException e) {
+ // write default config to game dir. Call th.is method again to read config from it.
+ try {
+ createConfigJson();
+ }
+ catch (IOException e1) {
+ System.out.println("[Bootstrap] Unable to write config.json file");
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ }
+
+
+ private static void createConfigJson() throws IOException {
+ File configFile = new File(configDir);
+
+ if (!configFile.exists() || configFile.length() == 0L) {
+ WriteConfig.INSTANCE.invoke();
+ }
+ }
+
+ /**
+ * Reads DefaultConfig to populate the gameConfig
+ */
+ private static void initialiseConfig() {
+ for (Map.Entry entry : DefaultConfig.INSTANCE.getHashMap().entrySet()) {
+ gameConfig.set(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Will forcibly overwrite previously loaded config value.
+ *
+ * Key naming convention will be 'modName:propertyName'; if modName is null, the key will be just propertyName.
+ *
+ * @param value JsonValue (the key-value pair)
+ * @param modName module name, nullable
+ */
+ public static void setToGameConfigForced(JsonValue value, String modName) {
+ gameConfig.set((modName == null) ? value.name : modName+":"+value.name,
+ value.isArray() ? value.asDoubleArray() :
+ value.isDouble() ? value.asDouble() :
+ value.isBoolean() ? value.asBoolean() :
+ value.isLong() ? value.asInt() :
+ value.asString()
+ );
+ }
+
+ /**
+ * Will not overwrite previously loaded config value.
+ *
+ * Key naming convention will be 'modName:propertyName'; if modName is null, the key will be just propertyName.
+ *
+ * @param value JsonValue (the key-value pair)
+ * @param modName module name, nullable
+ */
+ public static void setToGameConfig(JsonValue value, String modName) {
+ String key = (modName == null) ? value.name : modName+":"+value.name;
+ if (gameConfig.get(key) == null) {
+ gameConfig.set(key,
+ value.isArray() ? value.asDoubleArray() :
+ value.isDouble() ? value.asDouble() :
+ value.isBoolean() ? value.asBoolean() :
+ value.isLong() ? value.asInt() :
+ value.asString()
+ );
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIGraphicsControlPanel.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIGraphicsControlPanel.kt
index 9e08433ab..9e77e4410 100644
--- a/src/net/torvald/terrarum/modulebasegame/ui/UIGraphicsControlPanel.kt
+++ b/src/net/torvald/terrarum/modulebasegame/ui/UIGraphicsControlPanel.kt
@@ -109,7 +109,8 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
UIItemSpinner(this, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), arg[3].toDouble(), spinnerWidth, numberToTextFunction = { "${((it as Double)*100).toInt()}%" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = {
App.setConfig(optionStr, it)
- } }
+ }
+ }
}
else if (args.startsWith("typeinint")) {
// val arg = args.split(',') // args: none
diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIPerformanceControlPanel.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIPerformanceControlPanel.kt
new file mode 100644
index 000000000..ff9c1be5b
--- /dev/null
+++ b/src/net/torvald/terrarum/modulebasegame/ui/UIPerformanceControlPanel.kt
@@ -0,0 +1,219 @@
+package net.torvald.terrarum.modulebasegame.ui
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.Input
+import com.badlogic.gdx.graphics.Camera
+import com.badlogic.gdx.graphics.Color
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import net.torvald.terrarum.App
+import net.torvald.terrarum.CommonResourcePool
+import net.torvald.terrarum.ceilInt
+import net.torvald.terrarum.langpack.Lang
+import net.torvald.terrarum.ui.*
+import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
+import net.torvald.unicode.TIMES
+
+/**
+ * Created by minjaesong on 2023-06-22.
+ */
+class UIPerformanceControlPanel(remoCon: UIRemoCon?) : UICanvas() {
+
+
+ private val linegap = 14
+ private val panelgap = 20
+
+ private val rowheight = 20 + linegap
+
+ private val h1MarginTop = 16
+ private val h1MarginBottom = 4
+
+ private val options = arrayOf(
+ arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"),
+ arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,5,120,5,60000"),
+ arrayOf("", { Lang["MENU_OPTIONS_PERFORMANCE"] }, "h1"),
+ arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"),
+ arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
+ )
+
+ private val optionsYpos = IntArray(options.size + 1)
+
+ init {
+ CommonResourcePool.addToLoadingList("gui_hrule") {
+ TextureRegionPack(Gdx.files.internal("assets/graphics/gui/hrule.tga"), 216, 20)
+ }
+ CommonResourcePool.loadAll()
+
+
+
+ var akku = 0
+ options.forEachIndexed { index, row ->
+ val option = row[2]
+
+ if (index > 0 && option == "h1") {
+ akku += h1MarginTop
+ }
+
+ optionsYpos[index] = akku
+
+ akku += when (option) {
+ "h1" -> rowheight + h1MarginBottom
+ else -> rowheight
+ }
+ }
+ optionsYpos[optionsYpos.lastIndex] = akku
+ }
+ override var width = 560
+ override var height = optionsYpos.last()
+
+ private val hrule = CommonResourcePool.getAsTextureRegionPack("gui_hrule")
+
+ private val spinnerWidth = 140
+ private val drawX = (Toolkit.drawWidth - width) / 2
+ private val drawY = (App.scr.height - height) / 2
+
+ // @return Pair of
+ private fun makeButton(args: String, x: Int, y: Int, optionName: String): Pair Unit> {
+ return if (args.startsWith("h1") || args.startsWith("p")) {
+ (object : UIItem(this, x, y) {
+ override val width = 1
+ override val height = 1
+ override fun dispose() {}
+ }) to { _, _ -> }
+ }
+ else if (args.startsWith("toggle")) {
+ UIItemToggleButton(this, x, y, spinnerWidth, App.getConfigBoolean(optionName)) to { it: UIItem, optionStr: String ->
+ (it as UIItemToggleButton).clickOnceListener = { _, _ ->
+ it.toggle()
+ App.setConfig(optionStr, it.getStatus())
+ }
+ }
+ }
+ else if (args.startsWith("spinner,")) {
+ val arg = args.split(',')
+ UIItemSpinner(this, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), spinnerWidth, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
+ (it as UIItemSpinner).selectionChangeListener = {
+ App.setConfig(optionStr, it)
+ }
+ }
+ }
+ else if (args.startsWith("spinnerd,")) {
+ val arg = args.split(',')
+ UIItemSpinner(this, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), arg[3].toDouble(), spinnerWidth, numberToTextFunction = { "${((it as Double)*100).toInt()}%" }) to { it: UIItem, optionStr: String ->
+ (it as UIItemSpinner).selectionChangeListener = {
+ App.setConfig(optionStr, it)
+ }
+ }
+ }
+ else if (args.startsWith("spinnerimul,")) {
+ val arg = args.split(',')
+ val mult = arg[4].toInt()
+ UIItemSpinner(this, x, y, App.getConfigInt(optionName) / mult, arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), spinnerWidth, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
+ (it as UIItemSpinner).selectionChangeListener = {
+ App.setConfig(optionStr, it.toInt() * mult)
+ }
+ }
+ }
+ else if (args.startsWith("typeinint")) {
+// val arg = args.split(',') // args: none
+ UIItemTextLineInput(this, x, y, spinnerWidth, { "${App.getConfigInt(optionName)}" }, InputLenCap(4, InputLenCap.CharLenUnit.CODEPOINTS), { it.headkey in Input.Keys.NUM_0..Input.Keys.NUM_9 || it.headkey == Input.Keys.BACKSPACE }) to { it: UIItem, optionStr: String ->
+ (it as UIItemTextLineInput).textCommitListener = {
+ App.setConfig(optionStr, it.toInt()) // HAXXX!!!
+ }
+ }
+ }
+ else if (args.startsWith("typeinres")) {
+ val keyWidth = optionName.substringBefore(',')
+ val keyHeight = optionName.substringAfter(',')
+ UIItemTextLineInput(this, x, y, spinnerWidth, { "${App.getConfigInt(keyWidth)}x${App.getConfigInt(keyHeight)}" }, InputLenCap(9, InputLenCap.CharLenUnit.CODEPOINTS), { it.headkey == Input.Keys.ENTER || it.headkey == Input.Keys.BACKSPACE || it.character?.matches(Regex("[0-9xX]")) == true }, UIItemTextButton.Companion.Alignment.CENTRE) to { it: UIItem, optionStr: String ->
+ (it as UIItemTextLineInput).textCommitListener = { text ->
+ val text = text.lowercase()
+ if (text.matches(Regex("""[0-9]+x[0-9]+"""))) {
+ it.markAsNormal()
+ val width = text.substringBefore('x').toInt()
+ val height = text.substringAfter('x').toInt()
+ App.setConfig(keyWidth, width)
+ App.setConfig(keyHeight, height)
+ }
+ else it.markAsInvalid()
+ }
+ }
+ }
+ else throw IllegalArgumentException(args)
+ }
+
+ private val optionControllers: List Unit>> = options.mapIndexed { index, strings ->
+ makeButton(options[index][2] as String,
+ drawX + width / 2 + panelgap,
+ drawY - 2 + optionsYpos[index],
+ options[index][0] as String
+ )
+ }
+
+ init {
+ optionControllers.forEachIndexed { i, it ->
+ it.second.invoke(it.first, options[i][0] as String)
+ addUIitem(it.first)
+ }
+ }
+
+ override fun updateUI(delta: Float) {
+ uiItems.forEach { it.update(delta) }
+ }
+
+ override fun renderUI(batch: SpriteBatch, camera: Camera) {
+ /*batch.color = Toolkit.Theme.COL_INACTIVE
+ Toolkit.drawBoxBorder(batch, drawX, drawY, width, height)
+
+ batch.color = CELL_COL
+ Toolkit.fillArea(batch, drawX, drawY, width, height)*/
+
+ options.forEachIndexed { index, strings ->
+ val mode = strings[2]
+
+ val font = if (mode == "h1") App.fontUITitle else App.fontGame
+
+ val label = (strings[1] as () -> String).invoke()
+ val labelWidth = font.getWidth(label)
+ batch.color = when (mode) {
+ "h1" -> Toolkit.Theme.COL_MOUSE_UP
+ "p" -> Color.LIGHT_GRAY
+ else -> Color.WHITE
+ }
+
+ val xpos = if (mode == "p" || mode == "h1")
+ drawX + (width - labelWidth)/2 // centre-aligned
+ else
+ drawX + width/2 - panelgap - labelWidth // right aligned at the middle of the panel, offsetted by panelgap
+
+ font.draw(batch, label, xpos.toFloat(), drawY + optionsYpos[index] - 2f)
+
+ // draw hrule
+ if (mode == "h1") {
+ val ruleWidth = ((width - 24 - labelWidth) / 2).toFloat()
+ batch.draw(hrule.get(0,0), xpos - 24f - ruleWidth, drawY + optionsYpos[index].toFloat(), ruleWidth, hrule.tileH.toFloat())
+ batch.draw(hrule.get(0,1), xpos + 24f + labelWidth, drawY + optionsYpos[index].toFloat(), ruleWidth, hrule.tileH.toFloat())
+ }
+ }
+ uiItems.forEach { it.render(batch, camera) }
+
+ if (App.getConfigBoolean("fx_streamerslayout")) {
+ val xstart = App.scr.width - App.scr.chatWidth
+
+ batch.color = Color(0x00f8ff_40)
+ Toolkit.fillArea(batch, xstart + 1, 1, App.scr.chatWidth - 2, App.scr.height - 2)
+
+ batch.color = Toolkit.Theme.COL_MOUSE_UP
+ Toolkit.drawBoxBorder(batch, xstart + 1, 1, App.scr.chatWidth - 2, App.scr.height - 2)
+
+ val overlayResTxt = "${(App.scr.chatWidth * App.scr.magn).ceilInt()}$TIMES${App.scr.windowH}"
+
+ App.fontGame.draw(batch, overlayResTxt,
+ (xstart + (App.scr.chatWidth - App.fontGame.getWidth(overlayResTxt)) / 2).toFloat(),
+ ((App.scr.height - App.fontGame.lineHeight) / 2).toFloat()
+ )
+ }
+ }
+
+ override fun dispose() {
+ }
+}
\ No newline at end of file
diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt b/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt
index a1a38addd..25be8b232 100644
--- a/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt
+++ b/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt
@@ -18,6 +18,7 @@ object UITitleRemoConYaml {
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
- MENU_LABEL_IME : net.torvald.terrarum.modulebasegame.ui.UIIMEConfig
- MENU_LABEL_LANGUAGE : net.torvald.terrarum.modulebasegame.ui.UITitleLanguage
+ - GAME_GENRE_MISC : net.torvald.terrarum.modulebasegame.ui.UIPerformanceControlPanel
- MENU_MODULES : net.torvald.terrarum.ModOptionsHost
- MENU_LABEL_RETURN+WRITETOCONFIG
- MENU_MODULES : net.torvald.terrarum.modulebasegame.ui.UITitleModules