completely abolishing GSON; new save format impl wip

This commit is contained in:
minjaesong
2021-08-23 16:55:51 +09:00
parent df1ebdf93d
commit e15d5c9b05
58 changed files with 421 additions and 2101 deletions

View File

@@ -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="/" />

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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;

View File

@@ -21,4 +21,6 @@ object DatasetOp {
fis.close()
return ret
}
}

View File

@@ -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) {

View File

@@ -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
)*/
)
}
/*

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()

View File

@@ -48,7 +48,6 @@ object CommandDict {
// Test codes
"bulletintest" to SetBulletin,
"gsontest" to GsonTest,
"tips" to PrintRandomTips,
"langtest" to LangTest,
"spawnball" to SpawnPhysTestBall,

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
)
)
}
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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")

View File

@@ -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() {

View File

@@ -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")
}
}
}*/

View File

@@ -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() {

View File

@@ -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
}
}
} }
}
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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))
}
}

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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()
}
}*/
}

View File

@@ -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 }

View File

@@ -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" />

View File

@@ -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>,