mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
367 lines
12 KiB
Java
367 lines
12 KiB
Java
package net.torvald.terrarum;
|
|
|
|
import com.badlogic.gdx.utils.JsonValue;
|
|
import net.torvald.terrarum.utils.JsonFetcher;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* The headless launcher for the game.
|
|
*
|
|
* The launcher retrieves the information about the host operating system, and reads the `config.json`
|
|
* to set up the flags for the JVM then launches the main game.
|
|
*
|
|
* Created by minjaesong on 2023-06-22.
|
|
*/
|
|
public class Principii {
|
|
|
|
private static KVHashMap gameConfig = new KVHashMap();
|
|
|
|
private static String OSName = System.getProperty("os.name");
|
|
|
|
private static String operationSystem;
|
|
/** %appdata%/Terrarum, without trailing slash */
|
|
private static String defaultDir;
|
|
/** defaultDir + "/config.json" */
|
|
private 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 extracmd0 = devMode ? " -ea" : "";
|
|
String OS = OSName.toUpperCase();
|
|
String CPUARCH = System.getProperty("os.arch").toUpperCase();
|
|
String runtimeRoot;
|
|
String runtimeArch;
|
|
if (!CPUARCH.equals("AMD64") && !CPUARCH.equals("X86_64") && !CPUARCH.equals("AARCH64")) { // macOS Rosetta2 reports X86_64
|
|
System.err.println("Unsupported CPU architecture: " + CPUARCH);
|
|
System.exit(1);
|
|
return;
|
|
}
|
|
else {
|
|
runtimeArch = (CPUARCH.equals("AMD64") || CPUARCH.equals("X86_64")) ? "x86" : "arm";
|
|
}
|
|
|
|
if (OS.contains("WIN")) {
|
|
if (runtimeArch.equals("arm")) {
|
|
System.err.println("Windows on Arm is not supported");
|
|
System.exit(1);
|
|
return;
|
|
}
|
|
|
|
runtimeRoot = "runtime-windows-" + runtimeArch;
|
|
}
|
|
else if (OS.contains("OS X") || OS.contains("MACOS")) { // OpenJDK for mac will still report "Mac OS X" with version number "10.16", "11.x", "12.x", "13.x", ...
|
|
runtimeRoot = "runtime-osx-" + runtimeArch;
|
|
extracmd0 += " -XstartOnFirstThread";
|
|
}
|
|
else {
|
|
runtimeRoot = "runtime-linux-" + runtimeArch;
|
|
extracmd0 += " -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd";
|
|
}
|
|
|
|
String runtime = new File("out/"+runtimeRoot+"/bin/Terrarum").getAbsolutePath(); // /bin/Terrarum is just a renamed version of /bin/java
|
|
System.out.println("Runtime path: "+runtime);
|
|
|
|
|
|
|
|
getDefaultDirRoot();
|
|
configDir = defaultDir + "/config.json";
|
|
|
|
initialiseConfig();
|
|
readConfigJson();
|
|
|
|
|
|
int xmx = getConfigInt("jvm_xmx");
|
|
String userDefinedExtraCmd0 = getConfigString("jvm_extra_cmd").trim();
|
|
if (!userDefinedExtraCmd0.isEmpty()) userDefinedExtraCmd0 = " "+userDefinedExtraCmd0;
|
|
|
|
// String[] cmd = (runtime+extracmd0+userDefinedExtraCmd0+" -Xms1G -Xmx"+xmx+"G -cp ./out/TerrarumBuild.jar net.torvald.terrarum.App").split(" ");
|
|
|
|
// use `_cp` property to load custom application
|
|
String cp;
|
|
try {
|
|
cp = getConfigString("_cp");
|
|
}
|
|
catch (NullPointerException e) {
|
|
cp = "";
|
|
}
|
|
if (cp.isBlank()) cp = "net.torvald.terrarum.App";
|
|
|
|
List<String> extracmds = Arrays.stream(extracmd0.split(" ")).toList();
|
|
List<String> userDefinedExtraCmds = Arrays.stream(userDefinedExtraCmd0.split(" +")).filter((it) -> !it.isBlank()).toList();
|
|
ArrayList<String> cmd0 = new ArrayList<>();
|
|
cmd0.add(runtime);
|
|
cmd0.addAll(extracmds);
|
|
cmd0.addAll(userDefinedExtraCmds);
|
|
// cmd0.add("-Dhttps.protocols=SSLv3,TLSv1.2,TLSv1.1,TLSv1");
|
|
// cmd0.add("-Djavax.net.debug=ssl:handshake:verbose");
|
|
cmd0.add("-Xms1G");
|
|
cmd0.add("-Xmx"+xmx+"G");
|
|
cmd0.add("-cp");
|
|
cmd0.add("./out/TerrarumBuild.jar");
|
|
cmd0.add(cp);
|
|
var cmd = cmd0.stream().filter((it) -> !it.isBlank()).toList();
|
|
|
|
System.out.println(cmd);
|
|
|
|
try {
|
|
ProcessBuilder pb = new ProcessBuilder(cmd);
|
|
pb.inheritIO();
|
|
System.exit(pb.start().waitFor());
|
|
}
|
|
catch (IOException e) {
|
|
e.printStackTrace();
|
|
throw new RuntimeException(e);
|
|
}
|
|
catch (InterruptedException e) {
|
|
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.
|
|
*/
|
|
private 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.
|
|
*/
|
|
private 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.
|
|
*/
|
|
private 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.
|
|
*/
|
|
private static boolean getConfigBoolean(String key) {
|
|
try {
|
|
Object cfg = getConfigMaster(key);
|
|
return ((boolean) cfg);
|
|
}
|
|
catch (NullPointerException keyNotFound) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*private 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);
|
|
}*/
|
|
|
|
private static double[] getConfigDoubleArray(String key) {
|
|
Object cfg = getConfigMaster(key);
|
|
return ((double[]) cfg);
|
|
}
|
|
|
|
private 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;
|
|
}
|
|
|
|
/*private 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<String, Object> 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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @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.
|
|
e.printStackTrace();
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Reads DefaultConfig to populate the gameConfig
|
|
*/
|
|
private static void initialiseConfig() {
|
|
for (Map.Entry<String, Object> 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
|
|
*/
|
|
private 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()
|
|
);
|
|
}
|
|
|
|
|
|
} |