mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
completely abolishing GSON; new save format impl wip
This commit is contained in:
1
.idea/artifacts/TerrarumBuild.xml
generated
1
.idea/artifacts/TerrarumBuild.xml
generated
@@ -46,7 +46,6 @@
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-macos.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jlayer-1.0.1-gdx.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jorbis-0.0.17.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gson-2.8.7.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/commons-codec-1.15.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-controllers-core-2.2.1.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-controllers-desktop-2.2.1.jar" path-in-jar="/" />
|
||||
|
||||
14
.idea/libraries/com_google_code_gson_gson_2_8_7.xml
generated
14
.idea/libraries/com_google_code_gson_gson_2_8_7.xml
generated
@@ -1,14 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="com.google.code.gson:gson:2.8.7" type="repository">
|
||||
<properties maven-id="com.google.code.gson:gson:2.8.7" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/gson-2.8.7.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$PROJECT_DIR$/lib/gson-2.8.7-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/gson-2.8.7-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -1,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.72'
|
||||
ext.kotlin_version = '1.5.21'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -14,6 +14,9 @@ apply plugin: 'java'
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'kotlin'
|
||||
plugins {
|
||||
kotlin("plugin.serialization") version "1.5.21"
|
||||
}
|
||||
|
||||
sourceSets.main.java.srcDirs = ['src'] // because I'm not setting up proper /src/main/java/...
|
||||
|
||||
@@ -31,6 +34,7 @@ dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
compile fileTree(dir: 'lib', include: ['*.jar'])
|
||||
implementation 'org.junit:junit-bom:5.2.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<orderEntry type="library" name="com.badlogicgames.gdx:gdx-backend-lwjgl3:1.10.0" level="project" />
|
||||
<orderEntry type="library" name="GetCpuName-src" level="project" />
|
||||
<orderEntry type="library" name="jxinput-1.0.0-javadoc" level="project" />
|
||||
<orderEntry type="library" name="gson-2.8.5-javadoc" level="project" />
|
||||
<orderEntry type="library" name="prtree" level="project" />
|
||||
<orderEntry type="library" name="TerranVirtualDisk-src" level="project" />
|
||||
<orderEntry type="library" name="Terrarum_Joise" level="project" />
|
||||
@@ -20,7 +19,6 @@
|
||||
<orderEntry type="library" name="gdx-controllers-core-2.2.1-javadoc" level="project" />
|
||||
<orderEntry type="library" name="gdx-controllers-desktop-2.2.1-javadoc" level="project" />
|
||||
<orderEntry type="library" name="org.apache.commons:commons-csv:1.8" level="project" />
|
||||
<orderEntry type="library" name="com.google.code.gson:gson:2.8.7" level="project" />
|
||||
<orderEntry type="library" name="gdx-controllers-core-2.2.1" level="project" />
|
||||
<orderEntry type="library" name="gdx-controllers-desktop-2.2.1" level="project" />
|
||||
<orderEntry type="library" name="jxinput-1.0.0" level="project" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,6 @@ module terrarum.terrarum {
|
||||
requires jdk.unsupported; // sun.misc.Unsafe
|
||||
|
||||
// kotlin
|
||||
requires kotlin.stdlib;
|
||||
requires kotlin.test;
|
||||
|
||||
// gdx
|
||||
@@ -22,7 +21,9 @@ module terrarum.terrarum {
|
||||
|
||||
// etc
|
||||
requires GetCpuName;
|
||||
requires com.google.gson;
|
||||
requires kotlinx.serialization.core.jvm;
|
||||
requires kotlinx.serialization.json;
|
||||
requires kotlinx.serialization.json.jvm;
|
||||
requires org.apache.commons.codec;
|
||||
requires commons.csv;
|
||||
requires jxinput;
|
||||
|
||||
@@ -21,4 +21,6 @@ object DatasetOp {
|
||||
fis.close()
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -12,12 +12,9 @@ import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException;
|
||||
import com.badlogic.gdx.utils.JsonValue;
|
||||
import com.badlogic.gdx.utils.ScreenUtils;
|
||||
import com.github.strikerx3.jxinput.XInputDevice;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.torvald.gdx.graphics.PixmapIO2;
|
||||
import net.torvald.getcpuname.GetCpuName;
|
||||
import net.torvald.terrarum.concurrent.ThreadExecutor;
|
||||
@@ -37,18 +34,10 @@ import net.torvald.terrarum.utils.JsonWriter;
|
||||
import net.torvald.terrarum.worlddrawer.CreateTileAtlas;
|
||||
import net.torvald.terrarumsansbitmap.gdx.GameFontBase;
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack;
|
||||
import net.torvald.util.ArrayListMap;
|
||||
import net.torvald.util.DebugTimers;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
|
||||
import static net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED;
|
||||
import java.util.*;
|
||||
import static net.torvald.terrarum.TerrarumKt.gdxClearAndSetBlend;
|
||||
import static net.torvald.terrarum.TerrarumKt.printStackTrace;
|
||||
|
||||
@@ -973,7 +962,7 @@ public class AppLoader implements ApplicationListener {
|
||||
File configFile = new File(configDir);
|
||||
|
||||
if (!configFile.exists() || configFile.length() == 0L) {
|
||||
JsonWriter.INSTANCE.writeToFile(DefaultConfig.INSTANCE.fetch(), configDir);
|
||||
JsonWriter.INSTANCE.writeToFile(DefaultConfig.INSTANCE.getHashMap(), configDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,12 +973,18 @@ public class AppLoader implements ApplicationListener {
|
||||
private static Boolean readConfigJson() {
|
||||
try {
|
||||
// read from disk and build config from it
|
||||
JsonObject jsonObject = JsonFetcher.INSTANCE.invoke(configDir);
|
||||
JsonValue map = JsonFetcher.INSTANCE.invoke(configDir);
|
||||
|
||||
// make config
|
||||
jsonObject.entrySet().forEach((entry) ->
|
||||
gameConfig.set(entry.getKey(), entry.getValue())
|
||||
);
|
||||
for (JsonValue entry = map.child; entry != null; entry = entry.next) {
|
||||
gameConfig.set(entry.name,
|
||||
entry.isArray() ? entry.asDoubleArray() :
|
||||
entry.isDouble() ? entry.asDouble() :
|
||||
entry.isBoolean() ? entry.asBoolean() :
|
||||
entry.isLong() ? entry.asInt() :
|
||||
entry.asString()
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1018,10 +1013,7 @@ public class AppLoader implements ApplicationListener {
|
||||
*/
|
||||
public static int getConfigInt(String key) {
|
||||
Object cfg = getConfigMaster(key);
|
||||
if (cfg instanceof JsonPrimitive)
|
||||
return ((JsonPrimitive) cfg).getAsInt();
|
||||
else
|
||||
return Integer.parseInt(((String) cfg));
|
||||
return ((int) cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1034,10 +1026,7 @@ public class AppLoader implements ApplicationListener {
|
||||
*/
|
||||
public static String getConfigString(String key) {
|
||||
Object cfg = getConfigMaster(key);
|
||||
if (cfg instanceof JsonPrimitive)
|
||||
return ((JsonPrimitive) cfg).getAsString();
|
||||
else
|
||||
return ((String) cfg);
|
||||
return ((String) cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1049,17 +1038,14 @@ public class AppLoader implements ApplicationListener {
|
||||
public static boolean getConfigBoolean(String key) {
|
||||
try {
|
||||
Object cfg = getConfigMaster(key);
|
||||
if (cfg instanceof JsonPrimitive)
|
||||
return ((JsonPrimitive) cfg).getAsBoolean();
|
||||
else
|
||||
return ((boolean) cfg);
|
||||
return ((boolean) cfg);
|
||||
}
|
||||
catch (NullPointerException keyNotFound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] getConfigIntArray(String key) {
|
||||
/*public static int[] getConfigIntArray(String key) {
|
||||
Object cfg = getConfigMaster(key);
|
||||
if (cfg instanceof JsonArray) {
|
||||
JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
|
||||
@@ -1072,24 +1058,23 @@ public class AppLoader implements ApplicationListener {
|
||||
}
|
||||
else
|
||||
return ((int[]) cfg);
|
||||
}
|
||||
}*/
|
||||
|
||||
public static float[] getConfigFloatArray(String key) {
|
||||
public static double[] getConfigDoubleArray(String key) {
|
||||
Object cfg = getConfigMaster(key);
|
||||
if (cfg instanceof JsonArray) {
|
||||
JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
|
||||
//return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt })
|
||||
float[] floatArray = new float[jsonArray.size()];
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
floatArray[i] = jsonArray.get(i).getAsInt();
|
||||
}
|
||||
return floatArray;
|
||||
}
|
||||
else
|
||||
return ((float[]) cfg);
|
||||
return ((double[]) cfg);
|
||||
}
|
||||
|
||||
public static String[] getConfigStringArray(String key) {
|
||||
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();
|
||||
@@ -1102,13 +1087,13 @@ public class AppLoader implements ApplicationListener {
|
||||
}
|
||||
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 JsonObject getDefaultConfig() {
|
||||
return DefaultConfig.INSTANCE.fetch();
|
||||
private static HashMap<String, Object> getDefaultConfig() {
|
||||
return DefaultConfig.INSTANCE.getHashMap();
|
||||
}
|
||||
|
||||
private static Object getConfigMaster(String key1) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import com.badlogic.gdx.Input
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import net.torvald.terrarum.blockproperties.Block
|
||||
|
||||
/**
|
||||
@@ -11,126 +10,110 @@ import net.torvald.terrarum.blockproperties.Block
|
||||
* Created by minjaesong on 2016-03-12.
|
||||
*/
|
||||
object DefaultConfig {
|
||||
fun fetch(): JsonObject {
|
||||
val jsonObject = JsonObject()
|
||||
|
||||
jsonObject.addProperty("displayfps", 0) // 0: no limit, non-zero: limit
|
||||
jsonObject.addProperty("usevsync", false)
|
||||
jsonObject.addProperty("screenwidth", TerrarumScreenSize.defaultW)
|
||||
jsonObject.addProperty("screenheight", TerrarumScreenSize.defaultH)
|
||||
jsonObject.addProperty("atlastexsize", 2048)
|
||||
val hashMap = hashMapOf<String, Any>(
|
||||
"displayfps" to 0, // 0: no limit, non-zero: limit
|
||||
"usevsync" to false,
|
||||
"screenwidth" to TerrarumScreenSize.defaultW,
|
||||
"screenheight" to TerrarumScreenSize.defaultH,
|
||||
"atlastexsize" to 2048,
|
||||
|
||||
"language" to AppLoader.getSysLang(),
|
||||
"notificationshowuptime" to 4000,
|
||||
"multithread" to true,
|
||||
"multithreadedlight" to false,
|
||||
|
||||
"showhealthmessageonstartup" to true,
|
||||
|
||||
"usexinput" to true, // when FALSE, LT+RT input on xbox controller is impossible
|
||||
|
||||
"config_gamepadkeyn" to 3,
|
||||
"config_gamepadkeyw" to 2,
|
||||
"config_gamepadkeys" to 0,
|
||||
"config_gamepadkeye" to 1, // xbox indices
|
||||
|
||||
"config_gamepadlup" to 4,
|
||||
"config_gamepadrup" to 5,
|
||||
"config_gamepadselect" to 6,
|
||||
"config_gamepadstart" to 7,
|
||||
|
||||
"config_gamepadltrigger" to 8,
|
||||
"config_gamepadrtrigger" to 9,
|
||||
"config_gamepadlthumb" to 10,
|
||||
"config_gamepadrthumb" to 11,
|
||||
|
||||
|
||||
//jsonObject.addProperty("imtooyoungtodie", false) // no perma-death
|
||||
jsonObject.addProperty("language", AppLoader.getSysLang())
|
||||
jsonObject.addProperty("notificationshowuptime", 4000)
|
||||
jsonObject.addProperty("multithread", true) // experimental!
|
||||
jsonObject.addProperty("multithreadedlight", false) // experimental!
|
||||
"config_gamepadaxislx" to 1,
|
||||
"config_gamepadaxisly" to 0,
|
||||
"config_gamepadaxisrx" to 3,
|
||||
"config_gamepadaxisry" to 2, // 0-1-2-3 but sometimes 3-2-1-0 ?! what the actual fuck?
|
||||
"config_gamepadtriggeraxis" to 4, // positive: LT, negative: RT (xbox pad)
|
||||
"config_gamepadtriggeraxis2" to 5, // just in case... (RT)
|
||||
|
||||
jsonObject.addProperty("showhealthmessageonstartup", true)
|
||||
// to accomodate shifted zero point of analog stick
|
||||
"gamepadaxiszeropoints" to doubleArrayOf(-0.011, -0.022, -0.033, -0.044),
|
||||
|
||||
// control-gamepad
|
||||
"gamepadlabelstyle" to "msxbone", // "nwii", "logitech", "sonyps", "msxb360", "msxbone"
|
||||
|
||||
// "config_key", "config_mouse", "config_gamepad" are keyword recognised by control setup UI
|
||||
// control-keyboard (GDX key codes,
|
||||
"config_keyup" to Input.Keys.E,
|
||||
"config_keyleft" to Input.Keys.S,
|
||||
"config_keydown" to Input.Keys.D,
|
||||
"config_keyright" to Input.Keys.F, // ESDF Masterrace
|
||||
|
||||
jsonObject.addProperty("usexinput", true) // when FALSE, LT+RT input on xbox controller is impossible
|
||||
"config_keymovementaux" to Input.Keys.A, // movement-auxiliary, or hookshot
|
||||
"config_keyinventory" to Input.Keys.Q,
|
||||
"config_keyinteract" to Input.Keys.R,
|
||||
"config_keyclose" to Input.Keys.C, // this or hard-coded ESC
|
||||
"config_keyzoom" to Input.Keys.Z,
|
||||
|
||||
jsonObject.addProperty("config_gamepadkeyn", 3)
|
||||
jsonObject.addProperty("config_gamepadkeyw", 2)
|
||||
jsonObject.addProperty("config_gamepadkeys", 0)
|
||||
jsonObject.addProperty("config_gamepadkeye", 1) // xbox indices
|
||||
"config_keygamemenu" to Input.Keys.TAB,
|
||||
"config_keyquicksel" to Input.Keys.SHIFT_LEFT, // pie menu is now LShift because GDX does not read CapsLock
|
||||
// Colemak, Workman and some typers use CapsLock as Backspace, Apple-JIS and HHKB has Control in place of CapsLock and often re-assigned to Command
|
||||
// so these keys are treated as the same.
|
||||
// FOR ~~FUCKS~~ERGONOMICS' SAKE DON'T USE CTRL AND ALT AS A KEY!
|
||||
"config_keyquickselalt" to intArrayOf(Input.Keys.BACKSPACE, Input.Keys.CONTROL_LEFT, Input.Keys.BACKSLASH),
|
||||
"config_mousequicksel" to Input.Buttons.MIDDLE, // middle click to open pie menu
|
||||
|
||||
jsonObject.addProperty("config_gamepadlup", 4)
|
||||
jsonObject.addProperty("config_gamepadrup", 5)
|
||||
jsonObject.addProperty("config_gamepadselect", 6)
|
||||
jsonObject.addProperty("config_gamepadstart", 7)
|
||||
"config_keyjump" to Input.Keys.SPACE,
|
||||
|
||||
jsonObject.addProperty("config_gamepadltrigger", 8)
|
||||
jsonObject.addProperty("config_gamepadrtrigger", 9)
|
||||
jsonObject.addProperty("config_gamepadlthumb", 10)
|
||||
jsonObject.addProperty("config_gamepadrthumb", 11)
|
||||
"config_keyquickslots" to (Input.Keys.NUM_0..Input.Keys.NUM_9).toList(),
|
||||
|
||||
"config_mouseprimary" to Input.Buttons.LEFT, // left mouse
|
||||
"config_mousesecondary" to Input.Buttons.RIGHT, // right mouse
|
||||
|
||||
|
||||
jsonObject.addProperty("config_gamepadaxislx", 1)
|
||||
jsonObject.addProperty("config_gamepadaxisly", 0)
|
||||
jsonObject.addProperty("config_gamepadaxisrx", 3)
|
||||
jsonObject.addProperty("config_gamepadaxisry", 2) // 0-1-2-3 but sometimes 3-2-1-0 ?! what the actual fuck?
|
||||
jsonObject.addProperty("config_gamepadtriggeraxis", 4) // positive: LT, negative: RT (xbox pad)
|
||||
jsonObject.addProperty("config_gamepadtriggeraxis2", 5) // just in case... (RT)
|
||||
"pcgamepadenv" to "console",
|
||||
|
||||
val axesZeroPoints = JsonArray(); axesZeroPoints.add(-0.011f); axesZeroPoints.add(-0.022f); axesZeroPoints.add(-0.033f); axesZeroPoints.add(-0.044f)
|
||||
jsonObject.add("gamepadaxiszeropoints", axesZeroPoints) // to accomodate shifted zero point of analog stick
|
||||
|
||||
jsonObject.addProperty("gamepadlabelstyle", "msxbone") // "nwii", "logitech", "sonyps", "msxb360", "msxbone"
|
||||
|
||||
// control-keyboard (GDX key codes)
|
||||
jsonObject.addProperty("config_keyup", Input.Keys.E)
|
||||
jsonObject.addProperty("config_keyleft", Input.Keys.S)
|
||||
jsonObject.addProperty("config_keydown", Input.Keys.D)
|
||||
jsonObject.addProperty("config_keyright", Input.Keys.F) // ESDF Masterrace
|
||||
|
||||
jsonObject.addProperty("config_keymovementaux", Input.Keys.A) // movement-auxiliary, or hookshot
|
||||
jsonObject.addProperty("config_keyinventory", Input.Keys.Q)
|
||||
jsonObject.addProperty("config_keyinteract", Input.Keys.R)
|
||||
jsonObject.addProperty("config_keyclose", Input.Keys.C) // this or hard-coded ESC
|
||||
jsonObject.addProperty("config_keyzoom", Input.Keys.Z)
|
||||
|
||||
jsonObject.addProperty("config_keygamemenu", Input.Keys.TAB)
|
||||
jsonObject.addProperty("config_keyquicksel", Input.Keys.SHIFT_LEFT) // pie menu is now LShift because GDX does not read CapsLock
|
||||
val keyquickselalt = JsonArray(); keyquickselalt.add(Input.Keys.BACKSPACE); keyquickselalt.add(Input.Keys.CONTROL_LEFT); keyquickselalt.add(Input.Keys.BACKSLASH)
|
||||
// Colemak, Workman and some typers use CapsLock as Backspace, Apple-JIS and HHKB has Control in place of CapsLock and often re-assigned to Command
|
||||
// so these keys are treated as the same.
|
||||
// FOR ~~FUCKS~~ERGONOMICS' SAKE DON'T USE CTRL AND ALT AS A KEY!
|
||||
jsonObject.add("config_keyquickselalt", keyquickselalt)
|
||||
jsonObject.addProperty("config_mousequicksel", Input.Buttons.MIDDLE) // middle click to open pie menu
|
||||
|
||||
jsonObject.addProperty("config_keyjump", Input.Keys.SPACE)
|
||||
|
||||
val keyquickslots = JsonArray(); for (i in Input.Keys.NUM_1..Input.Keys.NUM_9) keyquickslots.add(i); keyquickslots.add(Input.Keys.NUM_0) // NUM_1 to NUM_0
|
||||
jsonObject.add("config_keyquickslots", keyquickslots)
|
||||
|
||||
jsonObject.addProperty("config_mouseprimary", Input.Buttons.LEFT) // left mouse
|
||||
jsonObject.addProperty("config_mousesecondary", Input.Buttons.RIGHT) // right mouse
|
||||
//jsonObject.writeValue("safetywarning" to true,
|
||||
|
||||
|
||||
jsonObject.addProperty("pcgamepadenv", "console")
|
||||
"maxparticles" to 768,
|
||||
|
||||
//jsonObject.addProperty("safetywarning", true)
|
||||
"temperatureunit" to 1, // -1: american, 0: kelvin, 1: celcius
|
||||
|
||||
|
||||
jsonObject.addProperty("maxparticles", 768)
|
||||
|
||||
jsonObject.addProperty("temperatureunit", 1) // -1: american, 0: kelvin, 1: celcius
|
||||
// "fancy" graphics settings
|
||||
"fxdither" to true,
|
||||
"fxretro" to false,
|
||||
//"fx3dlut" to false,
|
||||
|
||||
|
||||
// "fancy" graphics settings
|
||||
jsonObject.addProperty("fxdither", true)
|
||||
jsonObject.addProperty("fxretro", false)
|
||||
//jsonObject.addProperty("fx3dlut", false)
|
||||
// settings regarding debugger
|
||||
/*"buildingmakerfavs" to arrayOf(
|
||||
Block.GLASS_CRUDE,
|
||||
Block.PLANK_NORMAL,
|
||||
Block.PLANK_BIRCH,
|
||||
Block.STONE_QUARRIED,
|
||||
Block.STONE_BRICKS,
|
||||
|
||||
|
||||
// settings regarding debugger
|
||||
val buildingMakerFavs = JsonArray()
|
||||
arrayOf(
|
||||
Block.GLASS_CRUDE,
|
||||
Block.PLANK_NORMAL,
|
||||
Block.PLANK_BIRCH,
|
||||
Block.STONE_QUARRIED,
|
||||
Block.STONE_BRICKS,
|
||||
|
||||
Block.STONE_TILE_WHITE,
|
||||
Block.TORCH,
|
||||
"wall@" + Block.PLANK_NORMAL,
|
||||
"wall@" + Block.PLANK_BIRCH,
|
||||
"wall@" + Block.GLASS_CRUDE
|
||||
).forEach {
|
||||
buildingMakerFavs.add(it)
|
||||
}
|
||||
jsonObject.add("buildingmakerfavs", buildingMakerFavs)
|
||||
|
||||
|
||||
return jsonObject
|
||||
}
|
||||
Block.STONE_TILE_WHITE,
|
||||
Block.TORCH,
|
||||
"wall@" + Block.PLANK_NORMAL,
|
||||
"wall@" + Block.PLANK_BIRCH,
|
||||
"wall@" + Block.GLASS_CRUDE
|
||||
)*/
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2018-05-18.
|
||||
*/
|
||||
interface GsonSerialisable {
|
||||
|
||||
/**
|
||||
* Will modify itself according to the input gson. Not sure it's even necessary so please test.
|
||||
*/
|
||||
fun read(gson: JsonObject)
|
||||
|
||||
}
|
||||
@@ -45,7 +45,8 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
|
||||
field = value
|
||||
}
|
||||
/** how many different planets/stages/etc. are thenre. Whole stages must be manually managed by YOU. */
|
||||
var gameworldCount = 0
|
||||
var gameworldIndices = ArrayList<Int>()
|
||||
|
||||
/** The actor the game is currently allowing you to control.
|
||||
*
|
||||
* Most of the time it'd be the "player", but think about the case where you have possessed
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
|
||||
typealias ItemValue = KVHashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2015-12-30.
|
||||
*/
|
||||
open class KVHashMap : GsonSerialisable {
|
||||
open class KVHashMap {
|
||||
|
||||
constructor() {
|
||||
hashMap = HashMap<String, Any>()
|
||||
@@ -49,8 +46,7 @@ open class KVHashMap : GsonSerialisable {
|
||||
|
||||
if (value == null) return null
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asInt
|
||||
// if (value is JsonPrimitive) return value.asInt
|
||||
|
||||
return value as Int
|
||||
}
|
||||
@@ -62,8 +58,7 @@ open class KVHashMap : GsonSerialisable {
|
||||
|
||||
if (value is Int)
|
||||
return value.toDouble()
|
||||
else if (value is JsonPrimitive)
|
||||
return value.asDouble
|
||||
// else if (value is JsonPrimitive) return value.asDouble
|
||||
|
||||
return value as Double
|
||||
}
|
||||
@@ -77,8 +72,7 @@ open class KVHashMap : GsonSerialisable {
|
||||
|
||||
if (value == null) return null
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asString
|
||||
// if (value is JsonPrimitive) return value.asString
|
||||
|
||||
return value as String
|
||||
}
|
||||
@@ -88,8 +82,7 @@ open class KVHashMap : GsonSerialisable {
|
||||
|
||||
if (value == null) return null
|
||||
|
||||
if (value is JsonPrimitive)
|
||||
return value.asBoolean
|
||||
// if (value is JsonPrimitive) return value.asBoolean
|
||||
|
||||
return value as Boolean
|
||||
}
|
||||
@@ -109,9 +102,4 @@ open class KVHashMap : GsonSerialisable {
|
||||
val cloneOfMap = hashMap.clone() as HashMap<String, Any>
|
||||
return KVHashMap(cloneOfMap)
|
||||
}
|
||||
|
||||
override fun read(gson: JsonObject) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,6 +62,8 @@ object ModMgr {
|
||||
val moduleInfo = HashMap<String, ModuleMetadata>()
|
||||
val entryPointClasses = ArrayList<ModuleEntryPoint>()
|
||||
|
||||
val loadOrder = ArrayList<String>()
|
||||
|
||||
init {
|
||||
// load modules
|
||||
val loadOrderCSVparser = CSVParser.parse(
|
||||
@@ -75,6 +77,7 @@ object ModMgr {
|
||||
|
||||
loadOrder.forEachIndexed { index, it ->
|
||||
val moduleName = it[0]
|
||||
this.loadOrder.add(moduleName)
|
||||
printmsg(this, "Loading module $moduleName")
|
||||
|
||||
try {
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.io.IOException
|
||||
*/
|
||||
object BlockCodex {
|
||||
|
||||
private var blockProps = HashMap<ItemID, BlockProp>()
|
||||
val blockProps = HashMap<ItemID, BlockProp>()
|
||||
|
||||
val dynamicLights = SortedArrayList<ItemID>() // does not include virtual ones
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import net.torvald.gdx.graphics.Cvec
|
||||
import net.torvald.random.XXHash32
|
||||
import net.torvald.terrarum.gameitem.ItemID
|
||||
import net.torvald.terrarum.gameworld.fmod
|
||||
import net.torvald.terrarum.serialise.toLittle
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-02-16.
|
||||
@@ -92,7 +91,7 @@ class BlockProp {
|
||||
|
||||
var material: String = ""
|
||||
|
||||
var rngBase0 = Math.random().toFloat() // initial cycle phase (xxxxFuncX)
|
||||
var rngBase1 = Math.random().toFloat() // flicker P0, etc
|
||||
var rngBase2 = Math.random().toFloat() // flicker P1, etc
|
||||
@Transient var rngBase0 = Math.random().toFloat() // initial cycle phase (xxxxFuncX)
|
||||
@Transient var rngBase1 = Math.random().toFloat() // flicker P0, etc
|
||||
@Transient var rngBase2 = Math.random().toFloat() // flicker P1, etc
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import java.io.IOException
|
||||
*/
|
||||
object WireCodex {
|
||||
|
||||
private var wireProps = HashMap<ItemID, WireProp>()
|
||||
val wireProps = HashMap<ItemID, WireProp>()
|
||||
|
||||
private val nullProp = WireProp()
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ object CommandDict {
|
||||
|
||||
// Test codes
|
||||
"bulletintest" to SetBulletin,
|
||||
"gsontest" to GsonTest,
|
||||
"tips" to PrintRandomTips,
|
||||
"langtest" to LangTest,
|
||||
"spawnball" to SpawnPhysTestBall,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.torvald.terrarum.controller
|
||||
|
||||
import net.torvald.terrarum.AppLoader.gamepadDeadzone
|
||||
import net.torvald.terrarum.AppLoader.getConfigFloatArray
|
||||
import net.torvald.terrarum.AppLoader.getConfigDoubleArray
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2019-02-09.
|
||||
@@ -49,7 +49,7 @@ interface TerrarumController {
|
||||
*/
|
||||
fun getAxis(index:Int): Float {
|
||||
val raw = getAxisRaw(index)
|
||||
val zero = if (index < 4) getConfigFloatArray("gamepadaxiszeropoints")[index] else 0f
|
||||
val zero = if (index < 4) getConfigDoubleArray("gamepadaxiszeropoints")[index] else 0.0
|
||||
val compensatedRaw = raw - zero
|
||||
val inDeadzone = Math.abs(compensatedRaw) < gamepadDeadzone
|
||||
|
||||
@@ -58,7 +58,7 @@ interface TerrarumController {
|
||||
|
||||
fun inDeadzone(axis: Int): Boolean {
|
||||
val ax = getAxisRaw(axis)
|
||||
val zero = if (axis < 4) getConfigFloatArray("gamepadaxiszeropoints")[axis] else 0f
|
||||
val zero = if (axis < 4) getConfigDoubleArray("gamepadaxiszeropoints")[axis] else 0.0
|
||||
|
||||
return Math.abs(ax - zero) < gamepadDeadzone
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ object FactionFactory {
|
||||
@Throws(IOException::class)
|
||||
fun create(module: String, path: String): Faction {
|
||||
val jsonObj = JsonFetcher(ModMgr.getFile(module, path))
|
||||
val factionObj = Faction(jsonObj.get("factionname").asString)
|
||||
val factionObj = Faction(jsonObj.getString("factionname"))
|
||||
|
||||
jsonObj.get("factionamicable").asJsonArray.forEach { factionObj.addFactionAmicable(it.asString) }
|
||||
jsonObj.get("factionneutral").asJsonArray.forEach { factionObj.addFactionNeutral(it.asString) }
|
||||
jsonObj.get("factionhostile").asJsonArray.forEach { factionObj.addFactionHostile(it.asString) }
|
||||
jsonObj.get("factionfearful").asJsonArray.forEach { factionObj.addFactionFearful(it.asString) }
|
||||
jsonObj.get("factionamicable").asStringArray().forEach { factionObj.addFactionAmicable(it) }
|
||||
jsonObj.get("factionneutral").asStringArray().forEach { factionObj.addFactionNeutral(it) }
|
||||
jsonObj.get("factionhostile").asStringArray().forEach { factionObj.addFactionHostile(it) }
|
||||
jsonObj.get("factionfearful").asStringArray().forEach { factionObj.addFactionFearful(it) }
|
||||
|
||||
return factionObj
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package net.torvald.terrarum.gamecontroller
|
||||
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
import java.util.*
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-07-28.
|
||||
*/
|
||||
@@ -16,17 +20,18 @@ object KeyLayout {
|
||||
init {
|
||||
layouts = HashMap<String, KeyLayoutClass>()
|
||||
|
||||
val json = JsonFetcher("./res/keylayout.json")
|
||||
json.entrySet().forEach { it ->
|
||||
val map = net.torvald.terrarum.utils.JsonFetcher("./res/keylayout.json")
|
||||
JsonFetcher.forEach(map) { name, entry ->
|
||||
layouts.put(
|
||||
it.key,
|
||||
name,
|
||||
KeyLayoutClass(
|
||||
it.value.asJsonObject.get("layout").asString,
|
||||
it.value.asJsonObject.get("name").asString,
|
||||
it.value.asJsonObject.get("capslock").asString
|
||||
entry.getString("layout"),
|
||||
entry.getString("name"),
|
||||
entry.getString("capslock")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import net.torvald.terrarum.blockproperties.Fluid
|
||||
import net.torvald.terrarum.gameactors.WireActor
|
||||
import net.torvald.terrarum.gameitem.ItemID
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.serialise.ReadLayerDataZip
|
||||
import net.torvald.util.SortedArrayList
|
||||
import org.dyn4j.geometry.Vector2
|
||||
import kotlin.experimental.and
|
||||
@@ -165,31 +164,10 @@ open class GameWorld : Disposable {
|
||||
/**
|
||||
* Load existing world
|
||||
*/
|
||||
internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) {
|
||||
/*internal constructor(worldIndex: Int, json: JsonObject) {
|
||||
this.worldIndex = worldIndex
|
||||
|
||||
layerTerrain = layerData.layerTerrain
|
||||
layerWall = layerData.layerWall
|
||||
//layerWire = layerData.layerWire
|
||||
|
||||
wallDamages = layerData.wallDamages
|
||||
terrainDamages = layerData.terrainDamages
|
||||
fluidTypes = layerData.fluidTypes
|
||||
fluidFills = layerData.fluidFills
|
||||
|
||||
//wiringBlocks = HashMap()
|
||||
wirings = HashMap()
|
||||
|
||||
spawnX = layerData.spawnX
|
||||
spawnY = layerData.spawnY
|
||||
|
||||
width = layerTerrain.width
|
||||
height = layerTerrain.height
|
||||
|
||||
|
||||
creationTime = creationTIME_T
|
||||
lastPlayTime = lastPlayTIME_T
|
||||
this.totalPlayTime = totalPlayTime
|
||||
// TODO setup layerTerrain, layerWall, etc. from the json
|
||||
|
||||
// before the renaming, update the name maps
|
||||
tileNumberToNameMap = HashMap<Int, ItemID>()
|
||||
@@ -200,7 +178,7 @@ open class GameWorld : Disposable {
|
||||
}
|
||||
|
||||
// perform renaming of tile layers
|
||||
val oldTileNumberToNameMap = layerData.tileNumberToNameMap
|
||||
val oldTileNumberToNameMap = /* todo */
|
||||
for (y in 0 until layerTerrain.height) {
|
||||
for (x in 0 until layerTerrain.width) {
|
||||
layerTerrain.unsafeSetTile(x, y, tileNameToNumberMap[oldTileNumberToNameMap[layerTerrain.unsafeGetTile(x, y)]]!!)
|
||||
@@ -212,7 +190,7 @@ open class GameWorld : Disposable {
|
||||
|
||||
// AN EXCEPTIONAL TERM: tilenum 0 is always redirected to Air tile, even if the tilenum for actual Air tile is not zero
|
||||
tileNumberToNameMap[0] = Block.AIR
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Get 2d array data of wire
|
||||
|
||||
@@ -26,7 +26,7 @@ object ItemCodex {
|
||||
* <ItemID or RefID for Actor, TheItem>
|
||||
* Will return corresponding Actor if ID >= ACTORID_MIN
|
||||
*/
|
||||
private val itemCodex = HashMap<ItemID, GameItem>()
|
||||
val itemCodex = HashMap<ItemID, GameItem>()
|
||||
val dynamicItemDescription = HashMap<ItemID, GameItem>()
|
||||
val dynamicToStaticTable = HashMap<ItemID, ItemID>()
|
||||
|
||||
@@ -119,5 +119,5 @@ object ItemCodex {
|
||||
|
||||
}
|
||||
|
||||
fun hasItem(itemID: Int): Boolean = dynamicItemDescription.containsKey(itemID)
|
||||
fun hasItem(itemID: ItemID): Boolean = dynamicItemDescription.containsKey(itemID)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class Material {
|
||||
|
||||
object MaterialCodex {
|
||||
|
||||
private var materialProps = HashMap<String, Material>()
|
||||
val materialProps = HashMap<String, Material>()
|
||||
private val nullMaterial = Material()
|
||||
|
||||
operator fun invoke(module: String, path: String) {
|
||||
|
||||
@@ -82,9 +82,10 @@ object Lang {
|
||||
* "<<STRING ID>>" = "<<LOCALISED TEXT>>"
|
||||
*/
|
||||
//println(json.entrySet())
|
||||
json.entrySet().forEach {
|
||||
langpack.put("${it.key}_$lang", it.value.asString)
|
||||
JsonFetcher.forEach(json) { key, value ->
|
||||
langpack.put("${key}_$lang", value.asString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun processPolyglotLangFile(file: File, lang: String) {
|
||||
@@ -106,12 +107,13 @@ object Lang {
|
||||
* (the array continues)
|
||||
*
|
||||
*/
|
||||
json.getAsJsonObject("resources").getAsJsonArray("data").forEach {
|
||||
JsonFetcher.forEach(json.get("resources").get("data")) { _, entry ->
|
||||
langpack.put(
|
||||
"${it.asJsonObject["n"].asString}_$lang",
|
||||
it.asJsonObject["s"].asString
|
||||
"${entry.getString("n")}_$lang",
|
||||
entry.getString("s")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
operator fun get(key: String): String {
|
||||
|
||||
@@ -303,7 +303,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
|
||||
// init map as chosen size
|
||||
val timeNow = System.currentTimeMillis() / 1000
|
||||
gameworld = GameWorldExtension(1, worldParams.width, worldParams.height, timeNow, timeNow, 0) // new game, so the creation time is right now
|
||||
gameworldCount++
|
||||
gameworldIndices.add(gameworld.worldIndex)
|
||||
world = gameworld
|
||||
|
||||
// generate terrain for the map
|
||||
|
||||
@@ -20,7 +20,7 @@ internal object ExportAV : ConsoleCommand {
|
||||
if (player == null) return
|
||||
|
||||
JsonWriter.writeToFile(
|
||||
player.actorValue,
|
||||
player,
|
||||
AppLoader.defaultDir + "/Exports/" + args[1] + ".json")
|
||||
|
||||
Echo("ExportAV: exported to " + args[1] + ".json")
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
package net.torvald.terrarum.modulebasegame.console
|
||||
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.console.ConsoleCommand
|
||||
import net.torvald.terrarum.console.Echo
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.serialise.WriteMeta
|
||||
import net.torvald.terrarum.utils.JsonWriter
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2017-07-18.
|
||||
*/
|
||||
object ExportLayerData : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
/*try {
|
||||
val outfile = WriteLayerDataZip()
|
||||
WriteWorldInfo()
|
||||
Echo("Layer data exported to ${outfile!!.canonicalPath}")
|
||||
try {
|
||||
val str = WriteMeta(Terrarum.ingame!! as TerrarumIngame).invoke()
|
||||
val writer = java.io.FileWriter(AppLoader.defaultDir + "/Exports/savegame.json", false)
|
||||
writer.write(str)
|
||||
writer.close()
|
||||
Echo("Exportlayer: exported to savegame.json")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
catch (e: IOException) {
|
||||
Echo("Exportlayer: IOException raised.")
|
||||
e.printStackTrace()
|
||||
EchoError("Layer data export failed; see console for error traces.")
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.io.IOException
|
||||
/**
|
||||
* Created by minjaesong on 2016-02-10.
|
||||
*/
|
||||
internal object GsonTest : ConsoleCommand {
|
||||
/*internal object GsonTest : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
|
||||
@@ -51,4 +51,4 @@ internal object GsonTest : ConsoleCommand {
|
||||
|
||||
Echo("Usage: gsontest filename-without-extension")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -5,7 +5,6 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
|
||||
import net.torvald.terrarum.console.ConsoleCommand
|
||||
import net.torvald.terrarum.console.Echo
|
||||
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||
import net.torvald.terrarum.serialise.ReadLayerDataZip
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
@@ -13,7 +12,7 @@ import java.io.File
|
||||
*/
|
||||
object ImportLayerData : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
if (args.size < 2) {
|
||||
/*if (args.size < 2) {
|
||||
ExportLayerData.printUsage()
|
||||
return
|
||||
}
|
||||
@@ -28,7 +27,7 @@ object ImportLayerData : ConsoleCommand {
|
||||
(Terrarum.ingame!!.world).spawnX * TILE_SIZED
|
||||
)
|
||||
|
||||
Echo("Successfully loaded ${args[1]}")
|
||||
Echo("Successfully loaded ${args[1]}")*/
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
@@ -2,13 +2,12 @@ package net.torvald.terrarum.modulebasegame.gameactors
|
||||
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
import net.torvald.random.Fudge3
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import com.google.gson.JsonObject
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gameactors.ActorValue
|
||||
import java.security.SecureRandom
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-03-25.
|
||||
@@ -27,46 +26,44 @@ object InjectCreatureRaw {
|
||||
operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) {
|
||||
val jsonObj = JsonFetcher(ModMgr.getPath(module, "creatures/$jsonFileName"))
|
||||
|
||||
jsonObj.keySet().filter { !it.startsWith("_") }.forEach { key ->
|
||||
|
||||
JsonFetcher.forEach(jsonObj) { key, value -> if (!key.startsWith("_")) {
|
||||
val diceRollers = ArrayList<String>()
|
||||
|
||||
jsonObj[key].let {
|
||||
if (it.isJsonPrimitive) {
|
||||
val raw = it.asString
|
||||
val lowraw = raw.toLowerCase()
|
||||
// can the value be cast to Boolean?
|
||||
if (lowraw == "true") actorValueRef[key] = true
|
||||
else if (lowraw == "false") actorValueRef[key] = false
|
||||
else {
|
||||
try {
|
||||
actorValueRef[key] =
|
||||
if (raw.contains('.')) it.asDouble
|
||||
else it.asInt
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
actorValueRef[key] = raw
|
||||
}
|
||||
if (!value.isArray && !value.isObject) {
|
||||
val raw = value.asString()
|
||||
val lowraw = raw.lowercase()
|
||||
// can the value be cast to Boolean?
|
||||
if (lowraw == "true") actorValueRef[key] = true
|
||||
else if (lowraw == "false") actorValueRef[key] = false
|
||||
else {
|
||||
try {
|
||||
actorValueRef[key] =
|
||||
if (raw.contains('.')) value.asDouble()
|
||||
else value.asLong().toInt()
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
actorValueRef[key] = raw
|
||||
}
|
||||
}
|
||||
else if (key.endsWith(JSONMULT) && it.isJsonArray) {
|
||||
diceRollers.add(key)
|
||||
}
|
||||
else {
|
||||
printdbg(this, "Unknown Creature Raw key: $key")
|
||||
}
|
||||
}
|
||||
else if (key.endsWith(JSONMULT) && value.isArray) {
|
||||
diceRollers.add(key)
|
||||
}
|
||||
else {
|
||||
printdbg(this, "Unknown Creature Raw key: $key")
|
||||
}
|
||||
|
||||
diceRollers.forEach { keymult ->
|
||||
val keybase = keymult.substring(0, keymult.length - 4)
|
||||
val baseValue = jsonObj[keybase].asDouble
|
||||
val baseValue = jsonObj[keybase].asDouble()
|
||||
val selected = Fudge3(SecureRandom()).rollForArray()
|
||||
val mult = jsonObj[keymult].asJsonArray.get(selected).asInt
|
||||
val mult = jsonObj[keymult].asIntArray()[selected]
|
||||
val realValue = baseValue * mult / 100.0
|
||||
|
||||
actorValueRef[keybase] = realValue
|
||||
actorValueRef[keybase + BUFF] = 1.0
|
||||
}
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.gameworld.WorldTime
|
||||
import net.torvald.terrarum.serialise.ReadLayerDataZip
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2018-07-03.
|
||||
@@ -10,7 +9,7 @@ import net.torvald.terrarum.serialise.ReadLayerDataZip
|
||||
class GameWorldExtension : GameWorld {
|
||||
|
||||
constructor(worldIndex: Int, width: Int, height: Int, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, width, height, creationTIME_T, lastPlayTIME_T, totalPlayTime)
|
||||
internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, layerData, creationTIME_T, lastPlayTIME_T, totalPlayTime)
|
||||
//internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, layerData, creationTIME_T, lastPlayTIME_T, totalPlayTime)
|
||||
|
||||
|
||||
val economy = GameEconomy()
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.blockproperties.Block
|
||||
import net.torvald.terrarum.itemproperties.ItemCodex
|
||||
import net.torvald.terrarum.modulebasegame.BuildingMaker
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
@@ -153,7 +154,19 @@ class UIBuildingMakerPenMenu(val parent: BuildingMaker): UICanvas() {
|
||||
|
||||
// draw blocks slot
|
||||
batch.color = blockCellCol
|
||||
val slotConfig = AppLoader.getConfigStringArray("buildingmakerfavs")
|
||||
val slotConfig = arrayOf(
|
||||
Block.GLASS_CRUDE,
|
||||
Block.PLANK_NORMAL,
|
||||
Block.PLANK_BIRCH,
|
||||
Block.STONE_QUARRIED,
|
||||
Block.STONE_BRICKS,
|
||||
|
||||
Block.STONE_TILE_WHITE,
|
||||
Block.TORCH,
|
||||
"wall@" + Block.PLANK_NORMAL,
|
||||
"wall@" + Block.PLANK_BIRCH,
|
||||
"wall@" + Block.GLASS_CRUDE
|
||||
)//AppLoader.getConfigStringArray("buildingmakerfavs")
|
||||
for (i in 0 until PALETTE_SIZE) {
|
||||
val x = blockCellPos[i].x.roundToInt().toFloat()
|
||||
val y = blockCellPos[i].y.roundToInt().toFloat()
|
||||
|
||||
@@ -2,10 +2,10 @@ package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||
import net.torvald.terrarum.serialise.ReadWorldInfo
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UIItem
|
||||
import java.util.*
|
||||
@@ -17,7 +17,7 @@ import java.util.*
|
||||
*/
|
||||
class UIItemPlayerInfoCell(
|
||||
parent: UICanvas,
|
||||
val saveInfo: ReadWorldInfo.SaveMetaData,
|
||||
val saveInfo: JsonValue,
|
||||
override val width: Int,
|
||||
initialX: Int,
|
||||
initialY: Int,
|
||||
@@ -50,18 +50,18 @@ class UIItemPlayerInfoCell(
|
||||
init {
|
||||
val cal = Calendar.getInstance()
|
||||
|
||||
cal.timeInMillis = saveInfo.creationTime * 1000
|
||||
cal.timeInMillis = saveInfo.getLong("creation_t") * 1000
|
||||
creationTimeStr = "${cal[Calendar.YEAR]}-" +
|
||||
"${cal[Calendar.MONTH].toString().padStart(2,'0')}-" +
|
||||
"${cal[Calendar.DATE].toString().padStart(2,'0')}"
|
||||
|
||||
cal.timeInMillis = saveInfo.lastPlayTime * 1000
|
||||
cal.timeInMillis = saveInfo.getLong("lastplay_t") * 1000
|
||||
modificationTimeStr = "${cal[Calendar.YEAR]}-" +
|
||||
"${cal[Calendar.MONTH].toString().padStart(2,'0')}-" +
|
||||
"${cal[Calendar.DATE].toString().padStart(2,'0')}"
|
||||
|
||||
|
||||
worldCountStr = Lang["CONTEXT_WORLD_COUNT"] + saveInfo.worldCount
|
||||
worldCountStr = Lang["CONTEXT_WORLD_COUNT"] + saveInfo.get("worlds").asIntArray().size
|
||||
worldCountStrWidth = AppLoader.fontGame.getWidth(worldCountStr)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip.FILE_FOOTER
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_FOOTER
|
||||
import net.torvald.terrarum.toHex
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2019-02-20.
|
||||
*/
|
||||
|
||||
object PayloadUtil {
|
||||
/**
|
||||
* InputStream must be located manually at the payload begin
|
||||
*
|
||||
* For the actual use case, take a look at the source of the [ReadLayerDataZip].
|
||||
*/
|
||||
fun readAll(inputStream: MarkableFileInputStream, footer: ByteArray = FILE_FOOTER): HashMap<String, TEMzPayload> {
|
||||
val pldBuffer4 = ByteArray(4)
|
||||
val pldBuffer6 = ByteArray(6)
|
||||
val pldBuffer8 = ByteArray(8)
|
||||
|
||||
val payloads = HashMap<String, TEMzPayload>()
|
||||
|
||||
var pldCnt = 1
|
||||
|
||||
while (true) {
|
||||
// read header and get payload's name
|
||||
inputStream.read(pldBuffer8)
|
||||
|
||||
// check if end of payload reached
|
||||
if (pldBuffer8.contentEquals(footer)) {
|
||||
break
|
||||
}
|
||||
|
||||
val payloadName = pldBuffer8.copyOfRange(4, 8).toString(Charset.forName("US-ASCII"))
|
||||
|
||||
AppLoader.printdbg(this, "Payload $pldCnt name: $payloadName") // maybe maybe related with buffer things?
|
||||
|
||||
// get uncompressed size
|
||||
inputStream.read(pldBuffer6)
|
||||
val uncompressedSize = pldBuffer6.toLittleInt48()
|
||||
|
||||
// get deflated size
|
||||
inputStream.mark(2147483647) // FIXME deflated stream cannot be larger than 2 GB
|
||||
// creep forward until we hit the PAYLOAD_FOOTER
|
||||
var compressedSize: Int = 0 // FIXME deflated stream cannot be larger than 2 GB
|
||||
// loop init
|
||||
inputStream.read(pldBuffer8)
|
||||
// loop main
|
||||
while (!pldBuffer8.contentEquals(PAYLOAD_FOOTER)) {
|
||||
val aByte = inputStream.read(); compressedSize += 1
|
||||
if (aByte == -1) throw InternalError("Unexpected end-of-file at payload $pldCnt")
|
||||
pldBuffer8.shiftLeftBy(1, aByte.toByte())
|
||||
}
|
||||
|
||||
// at this point, we should have correct size of deflated bytestream
|
||||
|
||||
AppLoader.printdbg(this, "Payload $pldCnt compressed size: $compressedSize")
|
||||
|
||||
val compressedBytes = ByteArray(compressedSize) // FIXME deflated stream cannot be larger than 2 GB
|
||||
inputStream.reset() // go back to marked spot
|
||||
inputStream.read(compressedBytes)
|
||||
|
||||
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
|
||||
// Thus, \0pLd + [10] must be either of these.
|
||||
|
||||
// put constructed payload into a container
|
||||
payloads.put(payloadName, TEMzPayload(uncompressedSize, compressedBytes))
|
||||
|
||||
// skip over to be aligned with the next payload
|
||||
inputStream.skip(8)
|
||||
|
||||
pldCnt += 1
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
private fun ByteArray.shiftLeftBy(size: Int, fill: Byte = 0.toByte()) {
|
||||
if (size == 0) {
|
||||
return
|
||||
}
|
||||
else if (size < 0) {
|
||||
throw IllegalArgumentException("This won't shift to right (size = $size)")
|
||||
}
|
||||
else if (size >= this.size) {
|
||||
Arrays.fill(this, 0.toByte())
|
||||
}
|
||||
else {
|
||||
for (c in size..this.lastIndex) {
|
||||
this[c - size] = this[c]
|
||||
}
|
||||
for (c in (this.size - size)..this.lastIndex) {
|
||||
this[c] = fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.toByteString(): String {
|
||||
val sb = StringBuilder()
|
||||
this.forEach {
|
||||
sb.append(it.toUint().toHex().takeLast(2))
|
||||
sb.append(' ')
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndex)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
data class TEMzPayload(val uncompressedSize: Long, val bytes: ByteArray) // FIXME deflated stream cannot be larger than 2 GB
|
||||
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
/**
|
||||
* Only being used by the title screen and the demoworld. This object may get deleted at any update
|
||||
*
|
||||
* Created by minjaesong on 2016-08-24.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
@Deprecated("TEMD is deprecated format; use TEMz which does compression")
|
||||
internal object ReadLayerData {
|
||||
|
||||
init {
|
||||
throw Error("TEMD is old and removed format; use TEMz which does compression")
|
||||
}
|
||||
|
||||
/*internal operator fun invoke(inputStream: InputStream, inWorld: GameWorldExtension? = null): GameWorldExtension {
|
||||
val magicBytes = ByteArray(4)
|
||||
val layerSizeBytes = ByteArray(1)
|
||||
val layerCountBytes = ByteArray(1)
|
||||
val worldWidthBytes = ByteArray(4)
|
||||
val worldHeightBytes = ByteArray(4)
|
||||
val spawnCoordXBytes = ByteArray(4)
|
||||
val spawnCoordYBytes = ByteArray(4)
|
||||
|
||||
// read header first
|
||||
inputStream.read(magicBytes)
|
||||
if (!Arrays.equals(magicBytes, WriteLayerData.MAGIC)) {
|
||||
throw IllegalArgumentException("File not a Layer Data")
|
||||
}
|
||||
|
||||
inputStream.read(layerSizeBytes)
|
||||
inputStream.read(layerCountBytes)
|
||||
inputStream.skip(2) // reserved bytes
|
||||
inputStream.read(worldWidthBytes)
|
||||
inputStream.read(worldHeightBytes)
|
||||
inputStream.read(spawnCoordXBytes)
|
||||
inputStream.read(spawnCoordYBytes)
|
||||
|
||||
val worldWidth = worldWidthBytes.toLittleInt()
|
||||
val worldHeight = worldHeightBytes.toLittleInt()
|
||||
val bytesPerTile = layerSizeBytes[0].toUint()
|
||||
val layerCount = layerCountBytes[0].toUint()
|
||||
val layerSize = worldWidth * worldHeight * bytesPerTile
|
||||
|
||||
val terrainLayerMSB = ByteArray(layerSize)
|
||||
val wallLayerMSB = ByteArray(layerSize)
|
||||
val terrainLayerLSB = ByteArray(layerSize / 2)
|
||||
val wallLayerLSB = ByteArray(layerSize / 2)
|
||||
var wireLayer: ByteArray? = null
|
||||
|
||||
inputStream.read(terrainLayerMSB)
|
||||
inputStream.read(wallLayerMSB)
|
||||
inputStream.read(terrainLayerLSB)
|
||||
inputStream.read(wallLayerLSB)
|
||||
|
||||
if (layerCount == 4) {
|
||||
wireLayer = ByteArray(layerSize)
|
||||
inputStream.read(wireLayer)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// create world out of tiles data
|
||||
|
||||
val retWorld = inWorld ?: GameWorldExtension(1, worldWidth, worldHeight, 0, 0, 0) // FIXME null TIME_T for the (partial) test to pass
|
||||
|
||||
retWorld.layerTerrain.data = terrainLayerMSB
|
||||
retWorld.layerWall.data = wallLayerMSB
|
||||
retWorld.layerTerrainLowBits.data = terrainLayerLSB
|
||||
retWorld.layerWallLowBits.data = wallLayerLSB
|
||||
|
||||
|
||||
retWorld.spawnX = spawnCoordXBytes.toLittleInt()
|
||||
retWorld.spawnY = spawnCoordYBytes.toLittleInt()
|
||||
|
||||
|
||||
return retWorld
|
||||
}
|
||||
|
||||
|
||||
internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int {
|
||||
if (b == null) {
|
||||
throw NullPointerException()
|
||||
} else if (off < 0 || len < 0 || len > b.size) {
|
||||
throw IndexOutOfBoundsException()
|
||||
} else if (len == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var c = read()
|
||||
if (c == -1) {
|
||||
return -1
|
||||
}
|
||||
b[0] = c.toByte()
|
||||
|
||||
var i = 1
|
||||
try {
|
||||
while (i < len) {
|
||||
c = read()
|
||||
if (c == -1) {
|
||||
break
|
||||
}
|
||||
b[i] = c.toByte()
|
||||
i++
|
||||
}
|
||||
} catch (ee: IOException) {
|
||||
}
|
||||
|
||||
return i
|
||||
}*/
|
||||
}
|
||||
|
||||
fun Int.toLittle() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, discarding the sign.*/
|
||||
fun Int.toULittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, preserving the sign. In other words, it converts int to short. */
|
||||
fun Int.toLittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.shr(8).and(0xFF).toByte()
|
||||
)
|
||||
fun Long.toLittle() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte(),
|
||||
this.ushr(32).and(0xFF).toByte(),
|
||||
this.ushr(40).and(0xFF).toByte(),
|
||||
this.ushr(48).and(0xFF).toByte(),
|
||||
this.ushr(56).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts long as 6-byte array, discarding the sign. */
|
||||
fun Long.toULittle48() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte(),
|
||||
this.ushr(32).and(0xFF).toByte(),
|
||||
this.ushr(40).and(0xFF).toByte()
|
||||
)
|
||||
fun Double.toLittle() = java.lang.Double.doubleToRawLongBits(this).toLittle()
|
||||
fun Boolean.toLittle() = byteArrayOf(if (this) 0xFF.toByte() else 0.toByte())
|
||||
|
||||
fun ByteArray.toLittleInt() =
|
||||
if (this.size != 4) throw Error("Array not in size of 4")
|
||||
else this[0].toUint() or
|
||||
this[1].toUint().shl(8) or
|
||||
this[2].toUint().shl(16) or
|
||||
this[3].toUint().shl(24)
|
||||
fun ByteArray.toULittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toUint().shl(8)
|
||||
fun ByteArray.toLittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toInt().shl(8)
|
||||
fun ByteArray.toLittleLong() =
|
||||
if (this.size != 8) throw Error("Array not in size of 8")
|
||||
else this[0].toUlong() or
|
||||
this[1].toUlong().shl(8) or
|
||||
this[2].toUlong().shl(16) or
|
||||
this[3].toUlong().shl(24) or
|
||||
this[4].toUlong().shl(32) or
|
||||
this[5].toUlong().shl(40) or
|
||||
this[6].toUlong().shl(48) or
|
||||
this[7].toUlong().shl(56)
|
||||
fun ByteArray.toLittleInt48() =
|
||||
if (this.size != 6) throw Error("Array not in size of 6")
|
||||
else this[0].toUlong() or
|
||||
this[1].toUlong().shl(8) or
|
||||
this[2].toUlong().shl(16) or
|
||||
this[3].toUlong().shl(24) or
|
||||
this[4].toUlong().shl(32) or
|
||||
this[5].toUlong().shl(40)
|
||||
fun ByteArray.toLittleFloat() = java.lang.Float.intBitsToFloat(this.toLittleInt())
|
||||
|
||||
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
|
||||
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
||||
|
||||
const val WORLD_GENERATOR_VERSION = 1
|
||||
@@ -1,224 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.utils.compression.Lzma
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
import net.torvald.terrarum.gameitem.ItemID
|
||||
import net.torvald.terrarum.gameworld.BlockAddress
|
||||
import net.torvald.terrarum.gameworld.BlockLayer
|
||||
import net.torvald.terrarum.gameworld.FluidType
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-08-24.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object ReadLayerDataLzma {
|
||||
|
||||
// FIXME TERRAIN DAMAGE UNTESTED
|
||||
|
||||
internal operator fun invoke(file: File): ReadLayerDataZip.LayerData {
|
||||
val inputStream = MarkableFileInputStream(FileInputStream(file))
|
||||
|
||||
|
||||
val magicBytes = ByteArray(4)
|
||||
|
||||
|
||||
//////////////////
|
||||
// FILE READING //
|
||||
//////////////////
|
||||
|
||||
|
||||
// read header first
|
||||
inputStream.read(magicBytes)
|
||||
if (!Arrays.equals(magicBytes, WriteLayerDataZip.MAGIC)) {
|
||||
throw IllegalArgumentException("File not a Layer Data")
|
||||
}
|
||||
|
||||
val versionNumber = inputStream.read(1)[0].toUint()
|
||||
val layerCount = inputStream.read(1)[0].toUint()
|
||||
val payloadCount = inputStream.read(1)[0].toUint()
|
||||
val compression = inputStream.read(1)[0].toUint()
|
||||
val generatorVer = inputStream.read(2).toULittleShort()
|
||||
val width = inputStream.read(4).toLittleInt()
|
||||
val height = inputStream.read(4).toLittleInt()
|
||||
val spawnAddress = inputStream.read(6).toLittleInt48()
|
||||
|
||||
if (compression != 2) throw IllegalArgumentException("Input file is not compressed as LZMA; it's using algorithm $compression")
|
||||
|
||||
printdbg(this, "Version number: $versionNumber")
|
||||
printdbg(this, "Layers count: $layerCount")
|
||||
printdbg(this, "Payloads count: $payloadCount")
|
||||
printdbg(this, "Compression: $compression")
|
||||
printdbg(this, "World generator version: $generatorVer")
|
||||
printdbg(this, "Dimension: ${width}x$height")
|
||||
|
||||
// read payloads
|
||||
|
||||
val payloads = PayloadUtil.readAll(inputStream)
|
||||
/*val pldBuffer4 = ByteArray(4)
|
||||
val pldBuffer6 = ByteArray(6)
|
||||
val pldBuffer8 = ByteArray(8)
|
||||
|
||||
val payloads = HashMap<String, TEMzPayload>()
|
||||
|
||||
|
||||
// TODO please test the read; write has been fixed up
|
||||
|
||||
for (pldCnt in 0 until payloadCount) {
|
||||
inputStream.read(pldBuffer4)
|
||||
|
||||
// check payload header
|
||||
if (!pldBuffer4.contentEquals(WriteLayerDataZip.PAYLOAD_HEADER))
|
||||
throw InternalError("Payload $pldCnt not found -- expected ${WriteLayerDataZip.PAYLOAD_HEADER.toByteString()}, got ${pldBuffer4.toByteString()}")
|
||||
|
||||
// get payload's name
|
||||
inputStream.read(pldBuffer4)
|
||||
val payloadName = pldBuffer4.toString(Charset.forName("US-ASCII"))
|
||||
|
||||
printdbg(this, "Payload $pldCnt name: $payloadName") // maybe maybe related with buffer things?
|
||||
|
||||
// get uncompressed size
|
||||
inputStream.read(pldBuffer6)
|
||||
val uncompressedSize = pldBuffer6.toLittleInt48()
|
||||
|
||||
// get deflated size
|
||||
inputStream.mark(2147483647) // FIXME deflated stream cannot be larger than 2 GB
|
||||
// creep forward until we hit the PAYLOAD_FOOTER
|
||||
var deflatedSize: Int = 0 // FIXME deflated stream cannot be larger than 2 GB
|
||||
// loop init
|
||||
inputStream.read(pldBuffer8)
|
||||
// loop main
|
||||
while (!pldBuffer8.contentEquals(WriteLayerDataZip.PAYLOAD_FOOTER)) {
|
||||
val aByte = inputStream.read(); deflatedSize += 1
|
||||
if (aByte == -1) throw InternalError("Unexpected end-of-file at payload $pldCnt")
|
||||
pldBuffer8.shiftLeftBy(1, aByte.toByte())
|
||||
}
|
||||
|
||||
// at this point, we should have correct size of deflated bytestream
|
||||
|
||||
printdbg(this, "Payload $pldCnt compressed size: $deflatedSize")
|
||||
|
||||
val deflatedBytes = ByteArray(deflatedSize) // FIXME deflated stream cannot be larger than 2 GB
|
||||
inputStream.reset() // go back to marked spot
|
||||
inputStream.read(deflatedBytes)
|
||||
|
||||
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
|
||||
// Thus, \0pLd + [10] must be either of these.
|
||||
|
||||
// put constructed payload into a container
|
||||
payloads.put(payloadName, TEMzPayload(uncompressedSize, deflatedBytes))
|
||||
|
||||
// skip over to be aligned with the next payload
|
||||
inputStream.skip(8)
|
||||
}
|
||||
|
||||
|
||||
// test for EOF
|
||||
inputStream.read(pldBuffer8)
|
||||
if (!pldBuffer8.contentEquals(WriteLayerDataZip.FILE_FOOTER))
|
||||
throw InternalError("Expected end-of-file, got not-so-end-of-file")*/
|
||||
|
||||
|
||||
//////////////////////
|
||||
// END OF FILE READ //
|
||||
//////////////////////
|
||||
|
||||
val worldSize = width.toLong() * height
|
||||
|
||||
val payloadBytes = HashMap<String, ByteArray>()
|
||||
|
||||
payloads.forEach { t, u ->
|
||||
val inflatedOS = ByteArrayOutputStream(u.uncompressedSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB
|
||||
|
||||
try {
|
||||
Lzma.decompress(ByteArrayInputStream(u.bytes), inflatedOS)
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
// keep it empty (zero-sized file was compressed)
|
||||
}
|
||||
|
||||
val inflatedFile = inflatedOS.toByteArray()
|
||||
|
||||
payloadBytes[t] = inflatedFile
|
||||
}
|
||||
|
||||
val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress)
|
||||
|
||||
val terrainDamages = HashMap<BlockAddress, Float>()
|
||||
val wallDamages = HashMap<BlockAddress, Float>()
|
||||
val fluidTypes = HashMap<BlockAddress, FluidType>()
|
||||
val fluidFills = HashMap<BlockAddress, Float>()
|
||||
val tileNumToName = HashMap<Int, ItemID>()
|
||||
|
||||
// parse terrain damages
|
||||
for (c in payloadBytes["TdMG"]!!.indices step 10) {
|
||||
val bytes = payloadBytes["TdMG"]!!
|
||||
|
||||
val tileAddr = bytes.sliceArray(c..c+5)
|
||||
val value = bytes.sliceArray(c+6..c+9)
|
||||
|
||||
terrainDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
|
||||
}
|
||||
|
||||
|
||||
// parse wall damages
|
||||
for (c in payloadBytes["WdMG"]!!.indices step 10) {
|
||||
val bytes = payloadBytes["WdMG"]!!
|
||||
|
||||
val tileAddr = bytes.sliceArray(c..c+5)
|
||||
val value = bytes.sliceArray(c+6..c+9)
|
||||
|
||||
wallDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
|
||||
}
|
||||
|
||||
|
||||
// TODO parse fluid(Types|Fills)
|
||||
|
||||
// TODO parse tileNumToName
|
||||
|
||||
|
||||
return ReadLayerDataZip.LayerData(
|
||||
BlockLayer(width, height, payloadBytes["WALL"]!!),
|
||||
BlockLayer(width, height, payloadBytes["TERR"]!!),
|
||||
|
||||
spawnPoint.first, spawnPoint.second,
|
||||
|
||||
wallDamages, terrainDamages, fluidTypes, fluidFills, tileNumToName
|
||||
)
|
||||
}
|
||||
|
||||
internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int {
|
||||
if (b == null) {
|
||||
throw NullPointerException()
|
||||
} else if (off < 0 || len < 0 || len > b.size) {
|
||||
throw IndexOutOfBoundsException()
|
||||
} else if (len == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var c = read()
|
||||
if (c == -1) {
|
||||
return -1
|
||||
}
|
||||
b[0] = c.toByte()
|
||||
|
||||
var i = 1
|
||||
try {
|
||||
while (i < len) {
|
||||
c = read()
|
||||
if (c == -1) {
|
||||
break
|
||||
}
|
||||
b[i] = c.toByte()
|
||||
i++
|
||||
}
|
||||
} catch (ee: IOException) {
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
import net.torvald.terrarum.gameitem.ItemID
|
||||
import net.torvald.terrarum.gameworld.BlockAddress
|
||||
import net.torvald.terrarum.gameworld.BlockLayer
|
||||
import net.torvald.terrarum.gameworld.FluidType
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import java.util.zip.Inflater
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-08-24.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object ReadLayerDataZip {
|
||||
|
||||
// FIXME TERRAIN DAMAGE UNTESTED
|
||||
|
||||
internal operator fun invoke(file: File): LayerData {
|
||||
val inputStream = MarkableFileInputStream(FileInputStream(file))
|
||||
|
||||
|
||||
val magicBytes = ByteArray(4)
|
||||
|
||||
|
||||
//////////////////
|
||||
// FILE READING //
|
||||
//////////////////
|
||||
|
||||
|
||||
// read header first
|
||||
inputStream.read(magicBytes)
|
||||
if (!Arrays.equals(magicBytes, WriteLayerDataZip.MAGIC)) {
|
||||
throw IllegalArgumentException("File not a Layer Data")
|
||||
}
|
||||
|
||||
val versionNumber = inputStream.read(1)[0].toUint()
|
||||
val layerCount = inputStream.read(1)[0].toUint()
|
||||
val payloadCount = inputStream.read(1)[0].toUint()
|
||||
val compression = inputStream.read(1)[0].toUint()
|
||||
val generatorVer = inputStream.read(2).toULittleShort()
|
||||
val width = inputStream.read(4).toLittleInt()
|
||||
val height = inputStream.read(4).toLittleInt()
|
||||
val spawnAddress = inputStream.read(6).toLittleInt48()
|
||||
|
||||
if (compression != 1) throw IllegalArgumentException("Input file is not compressed as DEFLATE; it's using algorithm $compression")
|
||||
|
||||
printdbg(this, "Version number: $versionNumber")
|
||||
printdbg(this, "Layers count: $layerCount")
|
||||
printdbg(this, "Payloads count: $payloadCount")
|
||||
printdbg(this, "Compression: $compression")
|
||||
printdbg(this, "World generator version: $generatorVer")
|
||||
printdbg(this, "Dimension: ${width}x$height")
|
||||
|
||||
// read payloads
|
||||
|
||||
val payloads = PayloadUtil.readAll(inputStream)
|
||||
/*val pldBuffer4 = ByteArray(4)
|
||||
val pldBuffer6 = ByteArray(6)
|
||||
val pldBuffer8 = ByteArray(8)
|
||||
|
||||
val payloads = HashMap<String, TEMzPayload>()
|
||||
|
||||
|
||||
// TODO please test the read; write has been fixed up
|
||||
|
||||
for (pldCnt in 0 until payloadCount) {
|
||||
inputStream.read(pldBuffer4)
|
||||
|
||||
// check payload header
|
||||
if (!pldBuffer4.contentEquals(WriteLayerDataZip.PAYLOAD_HEADER))
|
||||
throw InternalError("Payload $pldCnt not found -- expected ${WriteLayerDataZip.PAYLOAD_HEADER.toByteString()}, got ${pldBuffer4.toByteString()}")
|
||||
|
||||
// get payload's name
|
||||
inputStream.read(pldBuffer4)
|
||||
val payloadName = pldBuffer4.toString(Charset.forName("US-ASCII"))
|
||||
|
||||
printdbg(this, "Payload $pldCnt name: $payloadName") // maybe maybe related with buffer things?
|
||||
|
||||
// get uncompressed size
|
||||
inputStream.read(pldBuffer6)
|
||||
val uncompressedSize = pldBuffer6.toLittleInt48()
|
||||
|
||||
// get deflated size
|
||||
inputStream.mark(2147483647) // FIXME deflated stream cannot be larger than 2 GB
|
||||
// creep forward until we hit the PAYLOAD_FOOTER
|
||||
var deflatedSize: Int = 0 // FIXME deflated stream cannot be larger than 2 GB
|
||||
// loop init
|
||||
inputStream.read(pldBuffer8)
|
||||
// loop main
|
||||
while (!pldBuffer8.contentEquals(WriteLayerDataZip.PAYLOAD_FOOTER)) {
|
||||
val aByte = inputStream.read(); deflatedSize += 1
|
||||
if (aByte == -1) throw InternalError("Unexpected end-of-file at payload $pldCnt")
|
||||
pldBuffer8.shiftLeftBy(1, aByte.toByte())
|
||||
}
|
||||
|
||||
// at this point, we should have correct size of deflated bytestream
|
||||
|
||||
printdbg(this, "Payload $pldCnt compressed size: $deflatedSize")
|
||||
|
||||
val deflatedBytes = ByteArray(deflatedSize) // FIXME deflated stream cannot be larger than 2 GB
|
||||
inputStream.reset() // go back to marked spot
|
||||
inputStream.read(deflatedBytes)
|
||||
|
||||
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
|
||||
// Thus, \0pLd + [10] must be either of these.
|
||||
|
||||
// put constructed payload into a container
|
||||
payloads.put(payloadName, TEMzPayload(uncompressedSize, deflatedBytes))
|
||||
|
||||
// skip over to be aligned with the next payload
|
||||
inputStream.skip(8)
|
||||
}
|
||||
|
||||
|
||||
// test for EOF
|
||||
inputStream.read(pldBuffer8)
|
||||
if (!pldBuffer8.contentEquals(WriteLayerDataZip.FILE_FOOTER))
|
||||
throw InternalError("Expected end-of-file, got not-so-end-of-file")*/
|
||||
|
||||
|
||||
//////////////////////
|
||||
// END OF FILE READ //
|
||||
//////////////////////
|
||||
|
||||
val worldSize = width.toLong() * height
|
||||
|
||||
val payloadBytes = HashMap<String, ByteArray>()
|
||||
|
||||
payloads.forEach { t, u ->
|
||||
val inflatedFile = ByteArray(u.uncompressedSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB
|
||||
val inflater = Inflater()
|
||||
inflater.setInput(u.bytes, 0, u.bytes.size)
|
||||
val uncompLen = inflater.inflate(inflatedFile)
|
||||
|
||||
// just in case
|
||||
if (uncompLen.toLong() != u.uncompressedSize)
|
||||
throw InternalError("Payload $t DEFLATE size mismatch -- expected ${u.uncompressedSize}, got $uncompLen")
|
||||
|
||||
payloadBytes[t] = inflatedFile
|
||||
}
|
||||
|
||||
val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress)
|
||||
|
||||
val terrainDamages = HashMap<BlockAddress, Float>()
|
||||
val wallDamages = HashMap<BlockAddress, Float>()
|
||||
val fluidTypes = HashMap<BlockAddress, FluidType>()
|
||||
val fluidFills = HashMap<BlockAddress, Float>()
|
||||
val tileNumberToNameMap = HashMap<Int, ItemID>()
|
||||
|
||||
// parse terrain damages
|
||||
for (c in payloadBytes["TdMG"]!!.indices step 10) {
|
||||
val bytes = payloadBytes["TdMG"]!!
|
||||
|
||||
val tileAddr = bytes.sliceArray(c..c+5)
|
||||
val value = bytes.sliceArray(c+6..c+9)
|
||||
|
||||
terrainDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
|
||||
}
|
||||
|
||||
|
||||
// parse wall damages
|
||||
for (c in payloadBytes["WdMG"]!!.indices step 10) {
|
||||
val bytes = payloadBytes["WdMG"]!!
|
||||
|
||||
val tileAddr = bytes.sliceArray(c..c+5)
|
||||
val value = bytes.sliceArray(c+6..c+9)
|
||||
|
||||
wallDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
|
||||
}
|
||||
|
||||
|
||||
// TODO parse fluid(Types|Fills)
|
||||
|
||||
|
||||
return LayerData(
|
||||
BlockLayer(width, height, payloadBytes["WALL"]!!),
|
||||
BlockLayer(width, height, payloadBytes["TERR"]!!),
|
||||
|
||||
spawnPoint.first, spawnPoint.second,
|
||||
|
||||
wallDamages, terrainDamages, fluidTypes, fluidFills, tileNumberToNameMap
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately deployable, a part of the gameworld
|
||||
*/
|
||||
internal data class LayerData(
|
||||
val layerWall: BlockLayer,
|
||||
val layerTerrain: BlockLayer,
|
||||
//val layerThermal: MapLayerHalfFloat, // in Kelvins
|
||||
//val layerAirPressure: MapLayerHalfFloat, // (milibar - 1000)
|
||||
|
||||
val spawnX: Int,
|
||||
val spawnY: Int,
|
||||
|
||||
//val wirings: HashMap<BlockAddress, SortedArrayList<GameWorld.WiringNode>>,
|
||||
val wallDamages: HashMap<BlockAddress, Float>,
|
||||
val terrainDamages: HashMap<BlockAddress, Float>,
|
||||
val fluidTypes: HashMap<BlockAddress, FluidType>,
|
||||
val fluidFills: HashMap<BlockAddress, Float>,
|
||||
val tileNumberToNameMap: HashMap<Int, ItemID>
|
||||
)
|
||||
|
||||
internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int {
|
||||
if (b == null) {
|
||||
throw NullPointerException()
|
||||
} else if (off < 0 || len < 0 || len > b.size) {
|
||||
throw IndexOutOfBoundsException()
|
||||
} else if (len == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var c = read()
|
||||
if (c == -1) {
|
||||
return -1
|
||||
}
|
||||
b[0] = c.toByte()
|
||||
|
||||
var i = 1
|
||||
try {
|
||||
while (i < len) {
|
||||
c = read()
|
||||
if (c == -1) {
|
||||
break
|
||||
}
|
||||
b[i] = c.toByte()
|
||||
i++
|
||||
}
|
||||
} catch (ee: IOException) {
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.*
|
||||
|
||||
|
||||
object ReadWorldInfo {
|
||||
|
||||
// FIXME UNTESTED
|
||||
|
||||
internal operator fun invoke(file: File): SaveMetaData {
|
||||
|
||||
val magic = ByteArray(4)
|
||||
val worldNameUTF8 = ArrayList<Byte>()
|
||||
|
||||
val fis = FileInputStream(file)
|
||||
|
||||
fis.read(magic)
|
||||
if (!Arrays.equals(magic, WriteWorldInfo.META_MAGIC)) {
|
||||
throw IllegalArgumentException("File not a Save Meta")
|
||||
}
|
||||
|
||||
|
||||
val descVersion = fis.read(1) // 0-127
|
||||
val numberOfHashes = fis.read() // 0-127
|
||||
|
||||
|
||||
var byteRead = fis.read()
|
||||
while (byteRead != 0) {
|
||||
if (byteRead == -1)
|
||||
throw InternalError("Unexpected EOF")
|
||||
|
||||
worldNameUTF8.add(byteRead.toByte())
|
||||
byteRead = fis.read()
|
||||
}
|
||||
|
||||
return SaveMetaData(
|
||||
String(worldNameUTF8.toByteArray(), Charsets.UTF_8),
|
||||
fis.read(8).toLittleLong(), // terrain seed
|
||||
fis.read(8).toLittleLong(), // rng s0
|
||||
fis.read(8).toLittleLong(), // rng s1
|
||||
fis.read(8).toLittleLong(), // weather s0
|
||||
fis.read(8).toLittleLong(), // weather s1
|
||||
fis.read(4).toLittleInt(), // player id
|
||||
fis.read(8).toLittleLong(), // world TIME_T
|
||||
fis.read(6).toLittleLong(), // creation time
|
||||
fis.read(6).toLittleLong(), // last play time
|
||||
fis.read(4).toLittleInt(), // total time wasted
|
||||
fis.read(32), // sha256sum worldinfo1
|
||||
fis.read(32), // sha256sum worldinfo2
|
||||
fis.read(32) // sha256sum worldinfo3
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
data class SaveMetaData(
|
||||
val worldName: String,
|
||||
val terrainSeed: Long,
|
||||
val rngS0: Long,
|
||||
val rngS1: Long,
|
||||
val weatherS0: Long,
|
||||
val weatherS1: Long,
|
||||
val playerID: Int,
|
||||
val timeNow: Long,
|
||||
val creationTime: Long,
|
||||
val lastPlayTime: Long,
|
||||
val totalPlayTime: Int,
|
||||
val worldinfo1Hash: ByteArray,
|
||||
val worldInfo2Hash: ByteArray,
|
||||
val worldInfo3Hash: ByteArray,
|
||||
|
||||
// gzipped TGA in meta
|
||||
val thumbnail: Texture = Texture(2, 2, Pixmap.Format.RGBA8888),
|
||||
// skim through the virtualdisk entries
|
||||
val worldCount: Int = 1,
|
||||
// read from the entry file
|
||||
val playerName: String = "Savegame",
|
||||
val playerWallet: Int = 0
|
||||
)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package net.torvald.terrarum.serialise
|
||||
/**
|
||||
* Created by minjaesong on 2018-10-03.
|
||||
*/
|
||||
object SavegameLoader {
|
||||
object SavegameReader {
|
||||
|
||||
// TODO read TEVd, load necessary shits
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.torvald.terrarum.gameactors.Actor
|
||||
import net.torvald.terrarum.gameitem.GameItem
|
||||
import net.torvald.terrarum.itemproperties.ItemCodex
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.*
|
||||
import net.torvald.terrarum.utils.JsonWriter.getJsonBuilder
|
||||
import net.torvald.util.SortedArrayList
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
@@ -77,76 +76,17 @@ object SavegameWriter {
|
||||
|
||||
// serialise current world (stage)
|
||||
|
||||
val worldBytes = WriteLayerDataZip(gameworld) // filename can be anything that is "world[n]" where [n] is any number
|
||||
if (worldBytes == null) {
|
||||
throw Error("Serialising world failed")
|
||||
}
|
||||
|
||||
if (!worldBytes.sliceArray(0..3).contentEquals(WriteLayerDataZip.MAGIC)) {
|
||||
worldBytes.forEach {
|
||||
print(it.toUInt().and(255u).toString(16).toUpperCase().padStart(2, '0'))
|
||||
print(' ')
|
||||
}; println()
|
||||
throw Error()
|
||||
}
|
||||
|
||||
// add current world (stage) to the disk
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
gameworld.worldIndex, ROOT,
|
||||
"world${gameworld.worldIndex}".toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(worldBytes)
|
||||
))
|
||||
|
||||
|
||||
|
||||
// TODO world[n] is done, needs whole other things
|
||||
|
||||
|
||||
// worldinfo0..3
|
||||
val worldinfoBytes = WriteWorldInfo(ingame)
|
||||
worldinfoBytes?.forEachIndexed { index, bytes ->
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
32766 - index, ROOT, "worldinfo$index".toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(bytes)
|
||||
))
|
||||
} ?: throw Error("Serialising worldinfo failed")
|
||||
|
||||
// loadorder.txt
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
32767, ROOT, "load_order.txt".toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(ByteArray64.fromByteArray(Gdx.files.internal("./assets/mods/LoadOrder.csv").readBytes()))
|
||||
))
|
||||
|
||||
// actors
|
||||
ingame.actorContainerActive.forEach {
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
rngPool.next(), ROOT,
|
||||
it.referenceID.toString(16).toUpperCase().toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(serialiseActor(it))
|
||||
))
|
||||
}
|
||||
ingame.actorContainerInactive.forEach {
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
rngPool.next(), ROOT,
|
||||
it.referenceID.toString(16).toUpperCase().toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(serialiseActor(it))
|
||||
))
|
||||
}
|
||||
|
||||
// items
|
||||
ItemCodex.dynamicItemDescription.forEach { dynamicID, item ->
|
||||
/*ItemCodex.dynamicItemDescription.forEach { dynamicID, item ->
|
||||
VDUtil.registerFile(disk, DiskEntry(
|
||||
rngPool.next(), ROOT,
|
||||
dynamicID.toByteArray(charset),
|
||||
creationDate, creationDate,
|
||||
EntryFile(serialiseItem(item))
|
||||
))
|
||||
}
|
||||
}*/
|
||||
|
||||
System.gc()
|
||||
|
||||
@@ -156,14 +96,79 @@ object SavegameWriter {
|
||||
fun modifyExistingSave(savefile: File): VirtualDisk {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
private fun serialiseActor(a: Actor): ByteArray64 {
|
||||
val gson = getJsonBuilder().toJson(a).toByteArray(charset)
|
||||
return ByteArray64.fromByteArray(gson)
|
||||
}
|
||||
fun Int.toLittle() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, discarding the sign.*/
|
||||
fun Int.toULittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, preserving the sign. In other words, it converts int to short. */
|
||||
fun Int.toLittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.shr(8).and(0xFF).toByte()
|
||||
)
|
||||
fun Long.toLittle() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte(),
|
||||
this.ushr(32).and(0xFF).toByte(),
|
||||
this.ushr(40).and(0xFF).toByte(),
|
||||
this.ushr(48).and(0xFF).toByte(),
|
||||
this.ushr(56).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts long as 6-byte array, discarding the sign. */
|
||||
fun Long.toULittle48() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte(),
|
||||
this.ushr(32).and(0xFF).toByte(),
|
||||
this.ushr(40).and(0xFF).toByte()
|
||||
)
|
||||
fun Double.toLittle() = java.lang.Double.doubleToRawLongBits(this).toLittle()
|
||||
fun Boolean.toLittle() = byteArrayOf(if (this) 0xFF.toByte() else 0.toByte())
|
||||
|
||||
private fun serialiseItem(i: GameItem): ByteArray64 {
|
||||
val gson = getJsonBuilder().toJson(i).toByteArray(charset)
|
||||
return ByteArray64.fromByteArray(gson)
|
||||
}
|
||||
}
|
||||
fun ByteArray.toLittleInt() =
|
||||
if (this.size != 4) throw Error("Array not in size of 4")
|
||||
else this[0].toUint() or
|
||||
this[1].toUint().shl(8) or
|
||||
this[2].toUint().shl(16) or
|
||||
this[3].toUint().shl(24)
|
||||
fun ByteArray.toULittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toUint().shl(8)
|
||||
fun ByteArray.toLittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toInt().shl(8)
|
||||
fun ByteArray.toLittleLong() =
|
||||
if (this.size != 8) throw Error("Array not in size of 8")
|
||||
else this[0].toUlong() or
|
||||
this[1].toUlong().shl(8) or
|
||||
this[2].toUlong().shl(16) or
|
||||
this[3].toUlong().shl(24) or
|
||||
this[4].toUlong().shl(32) or
|
||||
this[5].toUlong().shl(40) or
|
||||
this[6].toUlong().shl(48) or
|
||||
this[7].toUlong().shl(56)
|
||||
fun ByteArray.toLittleInt48() =
|
||||
if (this.size != 6) throw Error("Array not in size of 6")
|
||||
else this[0].toUlong() or
|
||||
this[1].toUlong().shl(8) or
|
||||
this[2].toUlong().shl(16) or
|
||||
this[3].toUlong().shl(24) or
|
||||
this[4].toUlong().shl(32) or
|
||||
this[5].toUlong().shl(40)
|
||||
fun ByteArray.toLittleFloat() = java.lang.Float.intBitsToFloat(this.toLittleInt())
|
||||
|
||||
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
|
||||
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
||||
@@ -1,61 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-03-18.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object WriteCSV {
|
||||
val META_FILENAME_TILE = "worldinfo2"
|
||||
val META_FILENAME_ITEM = "worldinfo3"
|
||||
val META_FILENAME_MAT = "worldinfo4"
|
||||
|
||||
internal fun write(saveDirectoryName: String): Boolean {
|
||||
//val tileCSV = CSVFetcher.readCSVasString(BlockCodex.CSV_PATH)
|
||||
//val itemCSV = CSVFetcher.readCSVasString(ItemCodex.CSV_PATH)
|
||||
//val matCSV = CSVFetcher.readCSVasString(MaterialCodex.CSV_PATH)
|
||||
|
||||
val pathTile = Paths.get("${AppLoader.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_TILE}")
|
||||
val pathItem = Paths.get("${AppLoader.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_ITEM}")
|
||||
val pathMat = Paths.get("${AppLoader.defaultSaveDir}" +
|
||||
"/$saveDirectoryName/${META_FILENAME_MAT}")
|
||||
val tempPathTile = Files.createTempFile(pathTile.toString(), "_temp")
|
||||
val tempPathItem = Files.createTempFile(pathItem.toString(), "_temp")
|
||||
val tempPathMat = Files.createTempFile(pathMat.toString(), "_temp")
|
||||
|
||||
// TODO gzip
|
||||
|
||||
// write CSV to path
|
||||
//Files.write(tempPathTile, tileCSV.toByteArray(Charsets.UTF_8))
|
||||
//Files.write(tempPathItem, itemCSV.toByteArray(Charsets.UTF_8))
|
||||
//Files.write(tempPathMat, matCSV.toByteArray(Charsets.UTF_8))
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
Files.copy(tempPathTile, pathTile, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathTile)
|
||||
|
||||
Files.copy(tempPathItem, pathItem, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathItem)
|
||||
|
||||
Files.copy(tempPathMat, pathMat, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(tempPathMat)
|
||||
|
||||
println("Saved map data '${WriteLayerData.LAYERS_FILENAME}' to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
/**
|
||||
* TODO this one does not use TerranVirtualDisk
|
||||
*
|
||||
* Created by minjaesong on 2016-03-18.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
@Deprecated("TEMD is old and removed format; use TEMz which does compression")
|
||||
internal object WriteLayerData {
|
||||
|
||||
init {
|
||||
throw Error("TEMD is old and removed format; use TEMz which does compression")
|
||||
}
|
||||
|
||||
val LAYERS_FILENAME = "worldinfo1"
|
||||
|
||||
/*val MAGIC = "TEMD".toByteArray(charset = Charset.forName("US-ASCII"))
|
||||
|
||||
val BYTE_NULL: Byte = 0
|
||||
|
||||
|
||||
internal operator fun invoke(saveDirectoryName: String): Boolean {
|
||||
val path = "${AppLoader.defaultSaveDir}/$saveDirectoryName/${LAYERS_FILENAME}"
|
||||
val tempPath = "${path}_bak"
|
||||
val map = (Terrarum.ingame!!.world)
|
||||
|
||||
val parentDir = File("${AppLoader.defaultSaveDir}/$saveDirectoryName")
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdir()
|
||||
}
|
||||
else if (!parentDir.isDirectory) {
|
||||
EchoError("Savegame directory is not actually a directory, aborting...")
|
||||
return false
|
||||
}
|
||||
|
||||
val tempFile = File(tempPath)
|
||||
val outFile = File(path)
|
||||
tempFile.createNewFile()
|
||||
|
||||
val outputStream = GZIPOutputStream(FileOutputStream(tempFile))
|
||||
|
||||
|
||||
// write binary
|
||||
outputStream.write(MAGIC)
|
||||
outputStream.write(byteArrayOf(GameWorld.SIZEOF))
|
||||
outputStream.write(byteArrayOf(GameWorld.LAYERS))
|
||||
outputStream.write(byteArrayOf(BYTE_NULL))
|
||||
outputStream.write(byteArrayOf(BYTE_NULL))
|
||||
outputStream.write(map.width.toLittle())
|
||||
outputStream.write(map.height.toLittle())
|
||||
outputStream.write(map.spawnX.toLittle())
|
||||
outputStream.write(map.spawnY.toLittle())
|
||||
// write one row (byteArray) at a time
|
||||
outputStream.write(map.layerTerrain.data)
|
||||
outputStream.write(map.layerWall.data)
|
||||
outputStream.write(map.layerTerrainLowBits.data)
|
||||
outputStream.write(map.layerWallLowBits.data)
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
||||
outFile.delete()
|
||||
tempFile.copyTo(outFile, overwrite = true)
|
||||
tempFile.delete()
|
||||
println("Saved map data '$LAYERS_FILENAME' to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
finally {
|
||||
outputStream.close()
|
||||
}
|
||||
|
||||
return false
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
const val WORLD_WRITER_FORMAT_VERSION = 3
|
||||
@@ -1,208 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.utils.compression.Lzma
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip.FILE_FOOTER
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_FOOTER
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_HEADER
|
||||
import java.io.*
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
|
||||
/**
|
||||
* This object only writes a file named 'worldinfo1'.
|
||||
*
|
||||
* The intended operation is as follows:
|
||||
* 1. This and others write
|
||||
*
|
||||
* TODO temporarily dump on the disk THEN pack? Or put all the files (in ByteArray64) in the RAM THEN pack?
|
||||
*
|
||||
* Created by minjaesong on 2016-03-18.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object WriteLayerDataLzma {
|
||||
|
||||
// FIXME TERRAIN DAMAGE UNTESTED
|
||||
|
||||
|
||||
// 2400x800 world size: about .. kB
|
||||
// 8192x2048 world size: about 470 kB but writes much slower than DEFLATE
|
||||
|
||||
|
||||
val LAYERS_FILENAME = "world"
|
||||
|
||||
val MAGIC = byteArrayOf(0x54, 0x45, 0x4D, 0x7A)
|
||||
val VERSION_NUMBER = WORLD_WRITER_FORMAT_VERSION.toByte()
|
||||
val NUMBER_OF_LAYERS = 3.toByte()
|
||||
val NUMBER_OF_PAYLOADS = 5.toByte()
|
||||
val COMPRESSION_ALGORITHM = 2.toByte()
|
||||
val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort()
|
||||
|
||||
//val NULL: Byte = 0
|
||||
|
||||
|
||||
/**
|
||||
* TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file.
|
||||
*
|
||||
* @return File on success; `null` on failure
|
||||
*/
|
||||
internal operator fun invoke(): File? {
|
||||
val world = (Terrarum.ingame!!.world)
|
||||
|
||||
val path = "${AppLoader.defaultSaveDir}/tmp_$LAYERS_FILENAME${world.worldIndex}"
|
||||
|
||||
// TODO let's try dump-on-the-disk-then-pack method...
|
||||
|
||||
/*val parentDir = File("${AppLoader.defaultSaveDir}/$saveDirectoryName")
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdir()
|
||||
}
|
||||
else if (!parentDir.isDirectory) {
|
||||
EchoError("Savegame directory is not actually a directory, aborting...")
|
||||
return false
|
||||
}*/
|
||||
|
||||
|
||||
val outFile = File(path)
|
||||
if (outFile.exists()) outFile.delete()
|
||||
outFile.createNewFile()
|
||||
|
||||
val outputStream = BufferedOutputStream(FileOutputStream(outFile), 8192)
|
||||
var deflater: DeflaterOutputStream // couldn't really use one outputstream for all the files.
|
||||
|
||||
fun wb(byteArray: ByteArray) { outputStream.write(byteArray) }
|
||||
fun wb(byte: Byte) { outputStream.write(byte.toInt()) }
|
||||
//fun wb(byte: Int) { outputStream.write(byte) }
|
||||
fun wi32(int: Int) { wb(int.toLittle()) }
|
||||
fun wi48(long: Long) { wb(long.toULittle48()) }
|
||||
fun wi64(long: Long) { wb(long.toLittle()) }
|
||||
fun wf32(float: Float) { wi32(float.toRawBits()) }
|
||||
|
||||
|
||||
////////////////////
|
||||
// WRITE BINARIES //
|
||||
////////////////////
|
||||
|
||||
|
||||
// all the necessary headers
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM); wb(GENERATOR_VERSION)
|
||||
|
||||
// world width, height, and spawn point
|
||||
wi32(world.width); wi32(world.height)
|
||||
wi48(LandUtil.getBlockAddr(world, world.spawnX, world.spawnY))
|
||||
|
||||
// write payloads //
|
||||
outputStream.flush()
|
||||
|
||||
// TERR payload
|
||||
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
|
||||
// Thus, \0pLd + [10] must be either of these.
|
||||
|
||||
wb(PAYLOAD_HEADER); wb("TERR".toByteArray())
|
||||
wi48(world.width * world.height * 3L / 2)
|
||||
world.layerTerrain.bytesIterator().forEach {
|
||||
val tempByteArray = ByteArray(1)
|
||||
tempByteArray[0] = it
|
||||
val tempByteArrayStream = ByteArrayInputStream(tempByteArray)
|
||||
Lzma.compress(tempByteArrayStream, outputStream)
|
||||
}
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WALL payload
|
||||
wb(PAYLOAD_HEADER); wb("WALL".toByteArray())
|
||||
wi48(world.width * world.height * 3L / 2)
|
||||
world.layerWall.bytesIterator().forEach {
|
||||
val tempByteArray = ByteArray(1)
|
||||
tempByteArray[0] = it
|
||||
val tempByteArrayStream = ByteArrayInputStream(tempByteArray)
|
||||
Lzma.compress(tempByteArrayStream, outputStream)
|
||||
}
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WIRE payload
|
||||
/*wb(PAYLOAD_HEADER); wb("WIRE".toByteArray())
|
||||
wi48(world.width * world.height.toLong())
|
||||
Lzma.compress(ByteArrayInputStream(world.wireArray), outputStream)
|
||||
wb(PAYLOAD_FOOTER)*/
|
||||
|
||||
// TdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("TdMG".toByteArray())
|
||||
wi48(world.terrainDamages.size * 10L)
|
||||
|
||||
|
||||
world.terrainDamages.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("WdMG".toByteArray())
|
||||
wi48(world.wallDamages.size * 10L)
|
||||
|
||||
|
||||
world.wallDamages.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlTP payload
|
||||
wb(PAYLOAD_HEADER); wb("FlTP".toByteArray())
|
||||
wi48(world.fluidTypes.size * 8L)
|
||||
|
||||
|
||||
world.fluidTypes.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.value.toLittleShort()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlFL payload
|
||||
wb(PAYLOAD_HEADER); wb("FlFL".toByteArray())
|
||||
wi48(world.fluidFills.size * 10L)
|
||||
|
||||
|
||||
world.fluidFills.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
|
||||
|
||||
// write footer
|
||||
wb(FILE_FOOTER)
|
||||
|
||||
|
||||
//////////////////
|
||||
// END OF WRITE //
|
||||
//////////////////
|
||||
|
||||
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
||||
|
||||
return outFile
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
finally {
|
||||
outputStream.close()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
|
||||
/**
|
||||
* This object only writes a file named 'worldinfo1'.
|
||||
*
|
||||
* The intended operation is as follows:
|
||||
* 1. This and others write
|
||||
*
|
||||
* TODO temporarily dump on the disk THEN pack? Or put all the files (in ByteArray64) in the RAM THEN pack?
|
||||
*
|
||||
* Created by minjaesong on 2016-03-18.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object WriteLayerDataZip {
|
||||
|
||||
// FIXME TERRAIN DAMAGE UNTESTED
|
||||
|
||||
|
||||
// 2400x800 world size, default comp level: about 90 kB
|
||||
// 8192x2048 world size, default comp level: about 670 kB
|
||||
|
||||
// 2400x800 world size, best comp level: about 75 kB
|
||||
// 8192x2048 world size, best comp level: about 555 kB
|
||||
|
||||
val LAYERS_FILENAME = "world"
|
||||
|
||||
val MAGIC = byteArrayOf(0x54, 0x45, 0x4D, 0x7A)
|
||||
val VERSION_NUMBER = WORLD_WRITER_FORMAT_VERSION.toByte()
|
||||
val NUMBER_OF_LAYERS = 3.toByte()
|
||||
val NUMBER_OF_PAYLOADS = 5.toByte()
|
||||
val COMPRESSION_ALGORITHM = 1.toByte()
|
||||
val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort()
|
||||
val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64) // \0pLd
|
||||
val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1) // EndPYLd\xFF
|
||||
val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2) // EndTEM with BOM
|
||||
|
||||
//val NULL: Byte = 0
|
||||
|
||||
|
||||
/**
|
||||
* @param world The world to serialise
|
||||
* @param path The directory where the temporary file goes, in relative to the AppLoader.defaultSaveDir. Should NOT start with slashed
|
||||
*
|
||||
* @return File on success; `null` on failure
|
||||
*/
|
||||
internal operator fun invoke(world: GameWorld): ByteArray64? {
|
||||
//val sanitisedPath = path.replace('\\', '/').removePrefix("/").replace("../", "")
|
||||
|
||||
//val path = "${AppLoader.defaultSaveDir}/$sanitisedPath/tmp_$LAYERS_FILENAME${world.worldIndex}"
|
||||
//val path = "${AppLoader.defaultSaveDir}/tmp_$LAYERS_FILENAME${world.worldIndex}"
|
||||
|
||||
// TODO let's try dump-on-the-disk-then-pack method...
|
||||
|
||||
/*val parentDir = File("${AppLoader.defaultSaveDir}/$saveDirectoryName")
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdir()
|
||||
}
|
||||
else if (!parentDir.isDirectory) {
|
||||
EchoError("Savegame directory is not actually a directory, aborting...")
|
||||
return false
|
||||
}*/
|
||||
|
||||
|
||||
//val outFile = File(path)
|
||||
//if (outFile.exists()) outFile.delete()
|
||||
//outFile.createNewFile()
|
||||
|
||||
//val outputStream = BufferedOutputStream(FileOutputStream(outFile), 8192)
|
||||
val outputStream = ByteArray64GrowableOutputStream()
|
||||
var deflater: DeflaterOutputStream // couldn't really use one outputstream for all the files.
|
||||
|
||||
fun wb(byteArray: ByteArray) { outputStream.write(byteArray) }
|
||||
fun wb(byte: Byte) { outputStream.write(byte.toInt()) }
|
||||
//fun wb(byte: Int) { outputStream.write(byte) }
|
||||
fun wi32(int: Int) { wb(int.toLittle()) }
|
||||
fun wi48(long: Long) { wb(long.toULittle48()) }
|
||||
fun wi64(long: Long) { wb(long.toLittle()) }
|
||||
fun wf32(float: Float) { wi32(float.toRawBits()) }
|
||||
|
||||
|
||||
////////////////////
|
||||
// WRITE BINARIES //
|
||||
////////////////////
|
||||
|
||||
|
||||
// all the necessary headers
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM); wb(GENERATOR_VERSION)
|
||||
|
||||
// world width, height, and spawn point
|
||||
wi32(world.width); wi32(world.height)
|
||||
wi48(LandUtil.getBlockAddr(world, world.spawnX, world.spawnY))
|
||||
|
||||
// write payloads //
|
||||
|
||||
// TERR payload
|
||||
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
|
||||
// Thus, \0pLd + [10] must be either of these.
|
||||
|
||||
// TODO serialised payloads have bit too much zeros, should I be worried?
|
||||
|
||||
val savePayloads = world.javaClass.superclass.declaredFields//.filter { it.isAnnotationPresent(TEMzPayload::class.java) }
|
||||
printdbg(this, "")
|
||||
savePayloads.forEach { printdbg(this, "${it.name}: ${it.type} @${it.declaredAnnotations.size}") }
|
||||
|
||||
wb(PAYLOAD_HEADER); wb("TERR".toByteArray())
|
||||
wi48(world.width * world.height * 3L / 2)
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
world.layerTerrain.bytesIterator().forEach { deflater.write(it.toInt()) }
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WALL payload
|
||||
wb(PAYLOAD_HEADER); wb("WALL".toByteArray())
|
||||
wi48(world.width * world.height * 3L / 2)
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
world.layerWall.bytesIterator().forEach { deflater.write(it.toInt()) }
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WIRE payload
|
||||
/*wb(PAYLOAD_HEADER); wb("WIRE".toByteArray())
|
||||
wi48(world.width * world.height.toLong())
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
deflater.write(world.wireArray)
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)*/
|
||||
|
||||
// TdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("TdMG".toByteArray())
|
||||
wi48(world.terrainDamages.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
|
||||
world.terrainDamages.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// WdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("WdMG".toByteArray())
|
||||
wi48(world.wallDamages.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
|
||||
world.wallDamages.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlTP payload
|
||||
wb(PAYLOAD_HEADER); wb("FlTP".toByteArray())
|
||||
wi48(world.fluidTypes.size * 8L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
|
||||
world.fluidTypes.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.value.toLittleShort())
|
||||
}
|
||||
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlFL payload
|
||||
wb(PAYLOAD_HEADER); wb("FlFL".toByteArray())
|
||||
wi48(world.fluidFills.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
|
||||
world.fluidFills.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
deflater.flush(); deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
|
||||
// write footer
|
||||
wb(FILE_FOOTER)
|
||||
|
||||
|
||||
//////////////////
|
||||
// END OF WRITE //
|
||||
//////////////////
|
||||
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
||||
return outputStream.toByteArray64()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,70 +1,73 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
|
||||
import java.nio.charset.Charset
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||
import net.torvald.terrarum.blockproperties.WireCodex
|
||||
import net.torvald.terrarum.itemproperties.ItemCodex
|
||||
import net.torvald.terrarum.itemproperties.MaterialCodex
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
|
||||
import net.torvald.terrarum.weather.WeatherMixer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-03-15.
|
||||
*/
|
||||
// internal for everything: prevent malicious module from messing up the savedata
|
||||
internal object WriteMeta {
|
||||
open class WriteMeta(val ingame: TerrarumIngame) {
|
||||
|
||||
val META_FILENAME = "worldinfo0"
|
||||
open fun invoke(): String {
|
||||
val world = ingame.world
|
||||
|
||||
val props = hashMapOf<String, Any>(
|
||||
"genver" to 4,
|
||||
"savename" to world.worldName,
|
||||
"terrseed" to world.generatorSeed,
|
||||
"randseed0" to RoguelikeRandomiser.RNG.state0,
|
||||
"randseed1" to RoguelikeRandomiser.RNG.state1,
|
||||
"weatseed0" to WeatherMixer.RNG.state0,
|
||||
"weatseed1" to WeatherMixer.RNG.state1,
|
||||
"playerid" to ingame.theRealGamer.referenceID,
|
||||
"creation_t" to world.creationTime,
|
||||
"lastplay_t" to world.lastPlayTime,
|
||||
"playtime_t" to world.totalPlayTime,
|
||||
|
||||
val MAGIC = "TESV".toByteArray(charset = Charset.forName("US-ASCII"))
|
||||
// CSVs
|
||||
"blocks" to StringBuilder().let {
|
||||
ModMgr.getFilesFromEveryMod("blocks/blocks.csv").forEach { (modname, file) ->
|
||||
it.append("\n\n## module: $modname ##\n\n")
|
||||
it.append(file.readText())
|
||||
}
|
||||
it.toString()
|
||||
},
|
||||
|
||||
"items" to StringBuilder().let {
|
||||
ModMgr.getFilesFromEveryMod("items/itemid.csv").forEach { (modname, file) ->
|
||||
it.append("\n\n## module: $modname ##\n\n")
|
||||
it.append(file.readText())
|
||||
}
|
||||
it.toString()
|
||||
},
|
||||
|
||||
val BYTE_NULL: Byte = 0
|
||||
"wires" to StringBuilder().let {
|
||||
ModMgr.getFilesFromEveryMod("wires/wires.csv").forEach { (modname, file) ->
|
||||
it.append("\n\n## module: $modname ##\n\n")
|
||||
it.append(file.readText())
|
||||
}
|
||||
it.toString()
|
||||
},
|
||||
|
||||
val terraseed: Long = Worldgen.params.seed
|
||||
// TODO fluids
|
||||
"materials" to StringBuilder().let {
|
||||
ModMgr.getFilesFromEveryMod("materials/materials.csv").forEach { (modname, file) ->
|
||||
it.append("\n\n## module: $modname ##\n\n")
|
||||
it.append(file.readText())
|
||||
}
|
||||
it.toString()
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Write save meta to specified directory. Returns false if something went wrong.
|
||||
* @param saveDirectoryName
|
||||
* @param savegameName -- Nullable. If the value is not specified, saveDirectoryName will be used instead.
|
||||
*/
|
||||
internal fun write(saveDirectoryName: String, savegameName: String?): Boolean {
|
||||
/*val hashArray: ArrayList<ByteArray> = ArrayList()
|
||||
val savenameAsByteArray: ByteArray =
|
||||
(savegameName ?: saveDirectoryName).toByteArray(Charsets.UTF_8)
|
||||
|
||||
// define Strings to be hashed
|
||||
val props = arrayOf(
|
||||
TilePropCSV()
|
||||
//, (item, mat, ...)
|
||||
"loadorder" to ModMgr.loadOrder,
|
||||
"worlds" to ingame.gameworldIndices
|
||||
)
|
||||
|
||||
// get and store hash from the list
|
||||
props.map { hashArray.add(DigestUtils.sha256(it)) }
|
||||
|
||||
// open file and delete it
|
||||
val metaPath = Paths.get("$AppLoader.defaultSaveDir" +
|
||||
"/$saveDirectoryName/$META_FILENAME")
|
||||
val metaTempPath = Files.createTempFile(metaPath.toString(), "_temp")
|
||||
|
||||
// TODO gzip
|
||||
|
||||
// write bytes in tempfile
|
||||
Files.write(metaTempPath, MAGIC)
|
||||
Files.write(metaTempPath, savenameAsByteArray)
|
||||
Files.write(metaTempPath, byteArrayOf(BYTE_NULL))
|
||||
Files.write(metaTempPath, toByteArray(terraseed))
|
||||
Files.write(metaTempPath, toByteArray(rogueseed))
|
||||
for (hash in hashArray)
|
||||
Files.write(metaTempPath, hash)
|
||||
|
||||
// replace savemeta with tempfile
|
||||
try {
|
||||
Files.copy(metaTempPath, metaPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.deleteIfExists(metaTempPath)
|
||||
println("Saved metadata to $saveDirectoryName.")
|
||||
|
||||
return true
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}*/
|
||||
return false
|
||||
|
||||
return Json(JsonWriter.OutputType.json).toJson(props)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.graphics.GL30
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||
import com.badlogic.gdx.utils.ScreenUtils
|
||||
import net.torvald.gdx.graphics.PixmapIO2
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||
import net.torvald.terrarum.weather.WeatherMixer
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64InputStream
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
|
||||
object WriteWorldInfo {
|
||||
|
||||
val META_MAGIC = "TESV".toByteArray(Charsets.UTF_8)
|
||||
val NULL = 0.toByte()
|
||||
|
||||
val VERSION = 1
|
||||
val HASHED_FILES_COUNT = 3
|
||||
|
||||
/**
|
||||
* TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file.
|
||||
*
|
||||
* @return List of ByteArray64, worldinfo0..worldinfo3; `null` on failure
|
||||
*/
|
||||
internal operator fun invoke(ingame: IngameInstance): List<ByteArray64>? {
|
||||
|
||||
//val path = "${AppLoader.defaultSaveDir}/tmp_worldinfo"
|
||||
|
||||
val infileList = arrayOf(
|
||||
ModMgr.getGdxFilesFromEveryMod("blocks/blocks.csv"),
|
||||
ModMgr.getGdxFilesFromEveryMod("items/items.csv"),
|
||||
ModMgr.getGdxFilesFromEveryMod("materials/materials.csv")
|
||||
)
|
||||
|
||||
val outFiles = ArrayList<ByteArray64>() // for worldinfo1-3 only
|
||||
val worldInfoHash = ArrayList<ByteArray>() // hash of worldinfo1-3
|
||||
// try to write worldinfo1-3
|
||||
|
||||
for (filenum in 1..HASHED_FILES_COUNT) {
|
||||
val outputStream = ByteArray64GrowableOutputStream()
|
||||
val infile = infileList[filenum - 1]
|
||||
|
||||
infile.forEach {
|
||||
outputStream.write("## from file: ${it.second.nameWithoutExtension()} ##############################\n".toByteArray())
|
||||
val readBytes = it.second.readBytes()
|
||||
outputStream.write(readBytes)
|
||||
outputStream.write("\n".toByteArray())
|
||||
}
|
||||
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
||||
|
||||
outFiles.add(outputStream.toByteArray64())
|
||||
|
||||
|
||||
worldInfoHash.add(DigestUtils.sha256(ByteArray64InputStream(outputStream.toByteArray64())))
|
||||
}
|
||||
|
||||
|
||||
// compose save meta (actual writing part)
|
||||
val metaOut = ByteArray64GrowableOutputStream()
|
||||
|
||||
|
||||
metaOut.write(META_MAGIC)
|
||||
metaOut.write(VERSION)
|
||||
metaOut.write(HASHED_FILES_COUNT)
|
||||
|
||||
// world name
|
||||
val world = ingame.world
|
||||
val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8)
|
||||
//metaOut.write(worldNameBytes)
|
||||
worldNameBytes.forEach {
|
||||
if (it != 0.toByte()) metaOut.write(it.toInt())
|
||||
}
|
||||
metaOut.write(NULL.toInt())
|
||||
|
||||
// terrain seed
|
||||
metaOut.write(world.generatorSeed.toLittle())
|
||||
|
||||
// randomiser seed
|
||||
metaOut.write(RoguelikeRandomiser.RNG.state0.toLittle())
|
||||
metaOut.write(RoguelikeRandomiser.RNG.state1.toLittle())
|
||||
|
||||
// weather seed
|
||||
metaOut.write(WeatherMixer.RNG.state0.toLittle())
|
||||
metaOut.write(WeatherMixer.RNG.state1.toLittle())
|
||||
|
||||
// reference ID of the player
|
||||
metaOut.write(Terrarum.PLAYER_REF_ID.toLittle())
|
||||
|
||||
// ingame time_t
|
||||
metaOut.write((world as GameWorldExtension).worldTime.TIME_T.toLittle())
|
||||
|
||||
// creation time (real world time)
|
||||
metaOut.write(world.creationTime.toULittle48())
|
||||
|
||||
// time at save (real world time)
|
||||
val timeNow = System.currentTimeMillis() / 1000L
|
||||
metaOut.write(timeNow.toULittle48())
|
||||
|
||||
// get playtime and save it
|
||||
val timeToAdd = (timeNow - world.loadTime).toInt()
|
||||
metaOut.write((world.totalPlayTime + timeToAdd).toLittle())
|
||||
world.lastPlayTime = timeNow
|
||||
world.totalPlayTime += timeToAdd
|
||||
|
||||
// SHA256SUM of worldinfo1-3
|
||||
worldInfoHash.forEach {
|
||||
metaOut.write(it)
|
||||
}
|
||||
|
||||
// thumbnail
|
||||
val texreg = ingame.actorGamer.sprite?.textureRegion
|
||||
if (texreg != null) {
|
||||
val batch = SpriteBatch()
|
||||
val camera = OrthographicCamera(texreg.tileW.toFloat(), texreg.tileH.toFloat())
|
||||
val fbo = FrameBuffer(Pixmap.Format.RGBA8888, texreg.tileW, texreg.tileH, false)
|
||||
|
||||
fbo.inAction(camera, batch) {
|
||||
batch.inUse {
|
||||
batch.draw(texreg.get(0, 0), 0f, 0f)
|
||||
}
|
||||
}
|
||||
|
||||
// bind and unbind the fbo so that I can get the damned Pixmap using ScreenUtils
|
||||
// NullPointerException if not appconfig.useGL30
|
||||
Gdx.gl30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, fbo.framebufferHandle)
|
||||
Gdx.gl30.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0)
|
||||
|
||||
val outpixmap = ScreenUtils.getFrameBufferPixmap(0, 0, fbo.width, fbo.height)
|
||||
|
||||
Gdx.gl30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0)
|
||||
Gdx.gl30.glReadBuffer(GL30.GL_BACK)
|
||||
|
||||
|
||||
val tgaSize = PixmapIO2.HEADER_FOOTER_SIZE + outpixmap.width * outpixmap.height * 4
|
||||
val byteArrayOS = ByteArrayOutputStream(tgaSize)
|
||||
PixmapIO2._writeTGA(byteArrayOS, outpixmap, true, true)
|
||||
byteArrayOS.flush()
|
||||
byteArrayOS.close()
|
||||
|
||||
|
||||
//PixmapIO2.writeTGA(Gdx.files.absolute(AppLoader.defaultDir+"/tmp_writeworldinfo+outpixmap.tga"), outpixmap, true)
|
||||
|
||||
|
||||
outpixmap.dispose()
|
||||
batch.dispose()
|
||||
fbo.dispose()
|
||||
|
||||
|
||||
|
||||
// write uncompressed size
|
||||
metaOut.write(tgaSize.toULittleShort())
|
||||
// write compressed tga
|
||||
val deflater = DeflaterOutputStream(metaOut, Deflater(Deflater.BEST_COMPRESSION, true), false)
|
||||
deflater.write(byteArrayOS.toByteArray())
|
||||
deflater.flush(); deflater.finish()
|
||||
// write footer
|
||||
metaOut.write(-1); metaOut.write(-2)
|
||||
}
|
||||
|
||||
// more data goes here //
|
||||
|
||||
|
||||
metaOut.flush()
|
||||
metaOut.close()
|
||||
|
||||
|
||||
|
||||
return listOf(metaOut.toByteArray64()) + outFiles.toList()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.torvald.terrarum.tests
|
||||
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
import net.torvald.terrarum.utils.JsonWriter
|
||||
import java.io.File
|
||||
@@ -63,7 +64,7 @@ fun main() {
|
||||
val key = record[0]
|
||||
val value = record[index + 2]
|
||||
if (value.isNotBlank()) {
|
||||
jsonObject.addProperty(key, value)
|
||||
jsonObject.addChild(key, JsonValue(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.torvald.terrarum.tests
|
||||
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||
import net.torvald.terrarum.serialise.WriteLayerDataZip
|
||||
import net.torvald.terrarum.serialise.toLittle
|
||||
import net.torvald.terrarum.serialise.toULittle48
|
||||
import java.util.zip.Deflater
|
||||
@@ -33,7 +32,7 @@ Ooh, black and yellow! Let's shake it up a little.""".trimIndent().toByteArray()
|
||||
|
||||
|
||||
|
||||
wb(WriteLayerDataZip.MAGIC); wb(WriteLayerDataZip.VERSION_NUMBER); wb(WriteLayerDataZip.NUMBER_OF_LAYERS); wb(WriteLayerDataZip.NUMBER_OF_PAYLOADS); wb(WriteLayerDataZip.COMPRESSION_ALGORITHM); wb(WriteLayerDataZip.GENERATOR_VERSION)
|
||||
//wb(WriteLayerDataZip.MAGIC); wb(WriteLayerDataZip.VERSION_NUMBER); wb(WriteLayerDataZip.NUMBER_OF_LAYERS); wb(WriteLayerDataZip.NUMBER_OF_PAYLOADS); wb(WriteLayerDataZip.COMPRESSION_ALGORITHM); wb(WriteLayerDataZip.GENERATOR_VERSION)
|
||||
val deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION), true)
|
||||
repeat(20001) {
|
||||
deflater.write(Math.random().times(256).toInt().and(255))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.torvald.terrarum.tests
|
||||
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import net.torvald.terrarum.utils.JsonWriter
|
||||
import org.dyn4j.geometry.Vector2
|
||||
|
||||
@@ -22,13 +23,12 @@ object GsonTest {
|
||||
|
||||
operator fun invoke() {
|
||||
|
||||
val gson = JsonWriter.getJsonBuilder()
|
||||
val jsonString = gson.toJson(testClass)
|
||||
val jsonString = Json(com.badlogic.gdx.utils.JsonWriter.OutputType.json).toJson(testClass)
|
||||
|
||||
println(jsonString)
|
||||
|
||||
val deserialised = Json().fromJson(GsonTestSuper::class.java, jsonString)
|
||||
|
||||
val deserialised = gson.fromJson(jsonString, GsonTestSuper::class.java)
|
||||
println(deserialised)
|
||||
println(deserialised as GsonTestClass) // ClassCastException
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class UINSMenu(
|
||||
|
||||
val listWidth = maxOf(
|
||||
AppLoader.fontGame.getWidth(menuTitle), minimumWidth,
|
||||
stringsFromTree.map { AppLoader.fontGame.getWidth(it) }.max() ?: 0
|
||||
stringsFromTree.map { AppLoader.fontGame.getWidth(it) }.maxOrNull() ?: 0
|
||||
)
|
||||
val uiWidth = listWidth + (2 * TEXT_OFFSETX.toInt())
|
||||
val listHeight = stringsFromTree.size * LINE_HEIGHT
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.torvald.terrarum.utils
|
||||
|
||||
import com.badlogic.gdx.utils.JsonReader
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
|
||||
/**
|
||||
@@ -10,7 +12,7 @@ object JsonFetcher {
|
||||
private var jsonString: StringBuffer? = null
|
||||
|
||||
@Throws(java.nio.file.NoSuchFileException::class)
|
||||
operator fun invoke(jsonFilePath: String): com.google.gson.JsonObject {
|
||||
operator fun invoke(jsonFilePath: String): JsonValue {
|
||||
jsonString = StringBuffer() // reset buffer every time it called
|
||||
readJsonFileAsString(jsonFilePath)
|
||||
|
||||
@@ -20,14 +22,11 @@ object JsonFetcher {
|
||||
throw Error("[JsonFetcher] jsonString is null!")
|
||||
}
|
||||
|
||||
val jsonParser = com.google.gson.JsonParser()
|
||||
val jsonObj = jsonParser.parse(jsonString.toString()).asJsonObject
|
||||
|
||||
return jsonObj
|
||||
return JsonReader().parse(jsonString.toString())
|
||||
}
|
||||
|
||||
@Throws(java.nio.file.NoSuchFileException::class)
|
||||
operator fun invoke(jsonFile: java.io.File): com.google.gson.JsonObject {
|
||||
operator fun invoke(jsonFile: java.io.File): JsonValue {
|
||||
jsonString = StringBuffer() // reset buffer every time it called
|
||||
readJsonFileAsString(jsonFile.canonicalPath)
|
||||
|
||||
@@ -37,10 +36,7 @@ object JsonFetcher {
|
||||
throw Error("[JsonFetcher] jsonString is null!")
|
||||
}
|
||||
|
||||
val jsonParser = com.google.gson.JsonParser()
|
||||
val jsonObj = jsonParser.parse(jsonString.toString()).asJsonObject
|
||||
|
||||
return jsonObj
|
||||
return JsonReader().parse(jsonString.toString())
|
||||
}
|
||||
|
||||
@Throws(java.nio.file.NoSuchFileException::class)
|
||||
@@ -50,4 +46,14 @@ object JsonFetcher {
|
||||
) // JSON does not require line break
|
||||
|
||||
}
|
||||
|
||||
fun forEach(map: JsonValue, action: (String, JsonValue) -> Unit) {
|
||||
var counter = 0
|
||||
var entry = map.child
|
||||
while (entry != null) {
|
||||
action(entry.name ?: "(arrayindex $counter)", entry)
|
||||
entry = entry.next
|
||||
counter += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.torvald.terrarum.utils
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-03-04.
|
||||
@@ -10,25 +10,6 @@ object JsonWriter {
|
||||
|
||||
private val formattingRegex = Regex("""(?<=[\{,\[])|(?=[\]}])""")
|
||||
|
||||
fun getJsonBuilder() = if (AppLoader.IS_DEVELOPMENT_BUILD) {
|
||||
getPrettyBuilder()
|
||||
}
|
||||
else {
|
||||
GsonBuilder()
|
||||
.serializeNulls()
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
.create()
|
||||
}
|
||||
|
||||
fun getPrettyBuilder() = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
|
||||
.serializeNulls()
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
.create()
|
||||
|
||||
/**
|
||||
* serialise a class to the file as JSON, using Google GSON.
|
||||
*
|
||||
@@ -37,10 +18,8 @@ object JsonWriter {
|
||||
*/
|
||||
@Throws(java.io.IOException::class)
|
||||
fun writeToFile(c: Any, path: String) {
|
||||
val jsonString = getJsonBuilder().toJson(c)
|
||||
|
||||
val writer = java.io.FileWriter(path, false)
|
||||
writer.write(jsonString.replace(formattingRegex, "\n"))
|
||||
writer.write(Json(JsonWriter.OutputType.json).toJson(c).replace(formattingRegex, "\n"))
|
||||
writer.close()
|
||||
}
|
||||
|
||||
@@ -50,13 +29,13 @@ object JsonWriter {
|
||||
* @param jsonObject
|
||||
* @param path: path to write a file
|
||||
*/
|
||||
@Throws(java.io.IOException::class)
|
||||
fun writeToFile(jsonObject: com.google.gson.JsonObject, path: String) {
|
||||
/*@Throws(java.io.IOException::class)
|
||||
fun writeToFile(jsonObject: Json, path: String) {
|
||||
val writer = java.io.FileWriter(path, false)
|
||||
|
||||
writer.write(getPrettyBuilder().toJson(jsonObject))
|
||||
writer.write(jsonObject.)
|
||||
//writer.write(jsonObject.toString().replace(formattingRegex, "\n"))
|
||||
writer.close()
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
@@ -275,37 +275,31 @@ internal object WeatherMixer : RNGConsumer {
|
||||
|
||||
val JSON = JsonFetcher(path)
|
||||
|
||||
val skyboxInJson = JSON.get("skyboxGradColourMap").asJsonPrimitive
|
||||
val extraImagesPath = JSON.getAsJsonArray("extraImages")
|
||||
val skyboxInJson = JSON.getString("skyboxGradColourMap")
|
||||
val extraImagesPath = JSON.get("extraImages").asStringArray()
|
||||
|
||||
|
||||
|
||||
val skybox = if (skyboxInJson.isString)
|
||||
GdxColorMap(ModMgr.getGdxFile("basegame", "$pathToImage/${skyboxInJson.asString}"))
|
||||
else
|
||||
throw IllegalStateException("In weather descriptor $path -- skyboxGradColourMap seems malformed.")
|
||||
val skybox = GdxColorMap(ModMgr.getGdxFile("basegame", "$pathToImage/${skyboxInJson}"))
|
||||
|
||||
|
||||
|
||||
val extraImages = ArrayList<Texture>()
|
||||
for (i in extraImagesPath)
|
||||
extraImages.add(Texture(ModMgr.getGdxFile("basegame", "$pathToImage/${i.asString}")))
|
||||
extraImages.add(Texture(ModMgr.getGdxFile("basegame", "$pathToImage/${i}")))
|
||||
|
||||
|
||||
|
||||
val classification = JSON.get("classification").asJsonPrimitive.asString
|
||||
val classification = JSON.getString("classification")
|
||||
|
||||
|
||||
|
||||
var mixFrom: String?
|
||||
try { mixFrom = JSON.get("mixFrom").asJsonPrimitive.asString }
|
||||
catch (e: NullPointerException) { mixFrom = null }
|
||||
try { mixFrom = JSON.getString("mixFrom") }
|
||||
catch (e: IllegalArgumentException) { mixFrom = null }
|
||||
|
||||
|
||||
|
||||
var mixPercentage: Double?
|
||||
try { mixPercentage = JSON.get("mixPercentage").asJsonPrimitive.asDouble }
|
||||
catch (e: NullPointerException) { mixPercentage = null }
|
||||
try { mixPercentage = JSON.getDouble("mixPercentage") }
|
||||
catch (e: IllegalArgumentException) { mixPercentage = null }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<orderEntry type="library" name="com.badlogicgames.gdx:gdx-backend-lwjgl3:1.10.0" level="project" />
|
||||
<orderEntry type="library" name="GetCpuName-src" level="project" />
|
||||
<orderEntry type="library" name="jxinput-1.0.0-javadoc" level="project" />
|
||||
<orderEntry type="library" name="gson-2.8.5-javadoc" level="project" />
|
||||
<orderEntry type="library" name="prtree" level="project" />
|
||||
<orderEntry type="library" name="TerranVirtualDisk-src" level="project" />
|
||||
<orderEntry type="library" name="Terrarum_Joise" level="project" />
|
||||
@@ -22,7 +21,6 @@
|
||||
<orderEntry type="library" name="gdx-controllers-desktop-2.2.1-javadoc" level="project" />
|
||||
<orderEntry type="library" name="org.apache.commons:commons-csv:1.8" level="project" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="com.google.code.gson:gson:2.8.7" level="project" />
|
||||
<orderEntry type="library" name="gdx-controllers-core-2.2.1" level="project" />
|
||||
<orderEntry type="library" name="gdx-controllers-desktop-2.2.1" level="project" />
|
||||
<orderEntry type="library" name="jxinput-1.0.0" level="project" />
|
||||
|
||||
@@ -10,8 +10,8 @@ Following code is an example savegame JSON files.
|
||||
weatseed: "e5e72beb4e3c6926d3dc9e3e2ef7833b",
|
||||
playerid: 9545698,
|
||||
creation_t: <creation time in real-world unix time>,
|
||||
lastplay_t: <creation time in real-world unix time>,
|
||||
creation_t: <creation time in real-world unix time>,
|
||||
lastplay_t: <last play time in real-world unix time>,
|
||||
playtime_t: <total play time in real-world unix time>,
|
||||
thumb: <Ascii85-encoded gzipped thumbnail image in TGA>,
|
||||
|
||||
blocks: <BlockCodex serialised>,
|
||||
|
||||
Reference in New Issue
Block a user