From e4caf297914b31a0bc6bbbf0f9cda4f2c08ccac4 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 31 Aug 2022 02:40:46 +0900 Subject: [PATCH] adding loadorder to savegame --- src/module-info.java | 2 + .../terrarum/debuggerapp/SavegameCracker.kt | 13 +- .../torvald/terrarum/savegame/VirtualDisk.kt | 24 ++-- .../terrarum/serialise/GameSavingThread.kt | 11 ++ .../torvald/terrarum/serialise/WriteActor.kt | 16 ++- .../DataFormats/SAVE_FORMAT.md | 10 +- work_files/DataFormats/Savegame container.txt | 27 ---- work_files/DataFormats/Savegame metadata.txt | 45 ------- work_files/DataFormats/Worldmap format.txt | 94 ------------- .../DataFormats/just-json-it-saveformat.md | 127 ------------------ .../terrarum_advanced_lang_file.txt | 47 +++++++ 11 files changed, 95 insertions(+), 321 deletions(-) rename SAVE_FORMAT.md => work_files/DataFormats/SAVE_FORMAT.md (90%) delete mode 100644 work_files/DataFormats/Savegame container.txt delete mode 100644 work_files/DataFormats/Savegame metadata.txt delete mode 100644 work_files/DataFormats/Worldmap format.txt delete mode 100644 work_files/DataFormats/just-json-it-saveformat.md create mode 100644 work_files/DataFormats/terrarum_advanced_lang_file.txt diff --git a/src/module-info.java b/src/module-info.java index bf4663bb5..055cd4493 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -65,4 +65,6 @@ module Terrarum { exports net.torvald.terrarum.modulebasegame.gameworld; exports net.torvald.terrarum.modulebasegame.ui; exports net.torvald.terrarum.modulebasegame.worldgenerator; + + exports net.torvald.terrarum.debuggerapp; } \ No newline at end of file diff --git a/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt b/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt index 0c480921a..5f7c746c4 100644 --- a/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt +++ b/src/net/torvald/terrarum/debuggerapp/SavegameCracker.kt @@ -147,13 +147,12 @@ class SavegameCracker( @Command("Lists contents of the disk") fun ls(args: List) { letdisk { - it.entries.forEach { i, entry -> - if (i != 0L) - println( - ccNoun + i.toString(10).padStart(11, ' ') + " " + - ccNoun2 + (diskIDtoReadableFilename(entry.entryID) + cc0).padEnd(24) { if (it == 0) ' ' else '.' } + - ccConst + " " + entry.contents.getSizePure() + " bytes" - ) + it.entries.toSortedMap().forEach { (i, entry) -> + if (i != 0L) println( + ccNoun + i.toString(10).padStart(11, ' ') + " " + + ccNoun2 + (diskIDtoReadableFilename(entry.entryID) + cc0).padEnd(24) { if (it == 0) ' ' else '.' } + + ccConst + " " + entry.contents.getSizePure() + " bytes" + ) } val entryCount = it.entries.size - 1 println("${cc0}$entryCount entries, total ${it.usedBytes} bytes") diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt index 6a9f6f452..b3e765e53 100644 --- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt +++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt @@ -236,18 +236,22 @@ class VirtualDisk( fun diskIDtoReadableFilename(id: EntryID): String = when (id) { 0L -> "root" -1L -> "savegameinfo.json" - -2L -> "thumbnail.tga.gz" - -16L -> "blockcodex.json.gz" - -17L -> "itemcodex.json.gz" - -18L -> "wirecodex.json.gz" - -19L -> "materialcodex.json.gz" - -20L -> "factioncodex.json.gz" - -1024L -> "apocryphas.json.gz" - in 1..65535 -> "worldinfo-$id.json" - in 1048576..2147483647 -> "actor-$id.json" + -2L -> "thumbnail.tga.gz (world)/spritedef (player)" + -3L -> "spritedef-glow (player)" + -4L -> "loadOrder.txt" +// -16L -> "blockcodex.json.gz" +// -17L -> "itemcodex.json.gz" +// -18L -> "wirecodex.json.gz" +// -19L -> "materialcodex.json.gz" +// -20L -> "factioncodex.json.gz" +// -1024L -> "apocryphas.json.gz" + -1025L -> "bodypart-to-entry.map" + -1026L -> "bodypartglow-to-entry.map" + in 1..65535 -> "bodypart #$id.tga.gz (player)" + in 1048576..2147483647 -> "actor #$id.json" in 0x0000_0001_0000_0000L..0x0000_FFFF_FFFF_FFFFL -> "World${id.ushr(32)}-L${id.and(0xFF00_0000).ushr(24)}-C${id.and(0xFFFFFF)}.gz" - else -> "file-$id" + else -> "file #$id" } class DiskEntry( diff --git a/src/net/torvald/terrarum/serialise/GameSavingThread.kt b/src/net/torvald/terrarum/serialise/GameSavingThread.kt index 649bbd560..de7627b5b 100644 --- a/src/net/torvald/terrarum/serialise/GameSavingThread.kt +++ b/src/net/torvald/terrarum/serialise/GameSavingThread.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum.serialise import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.ItemCodex +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.itemproperties.ItemRemapTable @@ -172,6 +173,16 @@ class WorldSavingThread( } + // write loadorder // + val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET) + loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n")) + loadOrderBa64Writer.flush(); loadOrderBa64Writer.close() + val loadOrderText = loadOrderBa64Writer.toByteArray64() + val loadOrderContents = EntryFile(loadOrderText) + addFile(disk, DiskEntry(-4L, 0L, creation_t, time_t, loadOrderContents)) + + + // Echo("Writing file to disk...") disk.entries[0]!!.modificationDate = time_t diff --git a/src/net/torvald/terrarum/serialise/WriteActor.kt b/src/net/torvald/terrarum/serialise/WriteActor.kt index 954e64cae..89d1e3dc2 100644 --- a/src/net/torvald/terrarum/serialise/WriteActor.kt +++ b/src/net/torvald/terrarum/serialise/WriteActor.kt @@ -2,19 +2,15 @@ package net.torvald.terrarum.serialise import net.torvald.spriteanimation.AssembledSpriteAnimation import net.torvald.spriteanimation.HasAssembledSprite -import net.torvald.spriteanimation.SpriteAnimation -import net.torvald.terrarum.spriteassembler.ADProperties import net.torvald.terrarum.ItemCodex +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.ActorWithBody -import net.torvald.terrarum.gameitems.GameItem -import net.torvald.terrarum.gameitems.ItemID -import net.torvald.terrarum.itemproperties.ItemRemapTable import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer -import net.torvald.terrarum.printStackTrace import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.spriteassembler.ADProperties import java.io.Reader import java.util.* @@ -111,7 +107,13 @@ object WritePlayer { addFile(playerDisk, DiskEntry(-3L, 0L, adlGlowCreationDate, time_t, adlGlowContents)) } - + // write loadorder // + val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET) + loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n")) + loadOrderBa64Writer.flush(); loadOrderBa64Writer.close() + val loadOrderText = loadOrderBa64Writer.toByteArray64() + val loadOrderContents = EntryFile(loadOrderText) + addFile(playerDisk, DiskEntry(-4L, 0L, jsonCreationDate, time_t, loadOrderContents)) } } diff --git a/SAVE_FORMAT.md b/work_files/DataFormats/SAVE_FORMAT.md similarity index 90% rename from SAVE_FORMAT.md rename to work_files/DataFormats/SAVE_FORMAT.md index 34301f996..a89c854a7 100644 --- a/SAVE_FORMAT.md +++ b/work_files/DataFormats/SAVE_FORMAT.md @@ -10,10 +10,11 @@ The main game directory is composed of following directories: - "${PlayerName}-${UUID}", TVDA { [-1] player JSON, [-2] spritedef, - [-3] optional spritedef-glow, - [-1025] sprite-bodypart-name-to-entry-number-map.properties, - [-1026] spriteglow-bodypart-name-to-entry-number-map.properties, - [1+] optional bodyparts tga.gz + [-3] !optional! spritedef-glow, + [-4] loadOrder.txt + [-1025] !optional! sprite-bodypart-name-to-entry-number-map.properties, + [-1026] !optional! spriteglow-bodypart-name-to-entry-number-map.properties, + [1+] !optional! bodyparts tga.gz } *if file -1025 is not there, read bodyparts from assets directory *optionally encrypt the files other than -1 @@ -27,6 +28,7 @@ The main game directory is composed of following directories: [actorID] actors (mainly fixtures) JSON, [0x1_0000_0000L or (layerNumber shl 24) or chunkNumber] chunk data, [-2] screenshot.tga.gz taken by the last player + [-4] loadOrder.txt } *disk name is world's name encoded in UTF-8 ``` diff --git a/work_files/DataFormats/Savegame container.txt b/work_files/DataFormats/Savegame container.txt deleted file mode 100644 index a7bbdf1b6..000000000 --- a/work_files/DataFormats/Savegame container.txt +++ /dev/null @@ -1,27 +0,0 @@ -A savegame consists of a Playable Character Information, Savegame Metadata, and other files. -A savegame is a single file in the format of TerranVirtualDisk. - -Files contained the TerranVirtualDisk is as follows: - -(root) - worldinfo0 -- Savegame Metadata (TESV) - Has fixed Entry ID of 32766 - worldinfo1 -- (TODO Copy of blocks.csv OR BlockCodex in JSON) -- will use this from the next load - Has fixed Entry ID of 32765 - worldinfo2 -- (TODO Copy of items.csv OR ItemCodex in JSON, static only) -- will use this from the next load - Has fixed Entry ID of 32764 - worldinfo3 -- (TODO Copy of materials.csv OR MaterialCodex in JSON) -- will use this from the next load - Has fixed Entry ID of 32763 - world[n] -- Layer Data (TEMD); [n] is a serial number of the world (starts at 1) - Has fixed Entry ID of [n] - (any random number in Hex ACTORID_MIN..FFFFFFFF) -- Serialised Entity Information (including Player), Entry ID is random - (PLAYER_REF_ID in Hex -- 91A7E2) -- Player Character Information (Serialised--JSON'd--Entity Information), Entry ID is random - (51621D) -- The Debug Player (Serialised Entity Information), Entry ID is random - load_order.txt -- LoadOrder.csv (NOT zipped) - Has fixed Entry ID of 32767 - - // TODO select one of following: - (any random number in Hex 32768..ACTORID_MIN - 1) -- Serialised Dynamic Item? - worldinfo4 -- dynamic item codex in JSON, has fixed Entry ID of 32762 - -Remarks: world history is created at the load time by scanning all the actors' corresponding ActorValue diff --git a/work_files/DataFormats/Savegame metadata.txt b/work_files/DataFormats/Savegame metadata.txt deleted file mode 100644 index 66bdb9daf..000000000 --- a/work_files/DataFormats/Savegame metadata.txt +++ /dev/null @@ -1,45 +0,0 @@ -Savegame metadata - -* Endianness: LITTLE -* Filename: 'worldinfo0' - -Ord Hex Description -00 54 T -01 45 E -02 4D S -03 44 V - -04 01 Descriptor version number - -05 03 Number of hashes - -06 Name of the world in UTF-8 (arbitrary length, must not contain NULL) -n-1 00 String terminator - -(Ord is now offset from n) - -00 Terrain seed (8 bytes) -08 Randomiser s0 (8 bytes) -10 Randomiser s1 (8 bytes) -18 Weather s0 (8 bytes) -20 Weather s1 (8 bytes) - -28 ReferenceID of the player (4 bytes, a fixed value of 91A7E2) -2C Current world's time_t (the ingame time, 8 bytes) - -34 Creation time in time_t (6 bytes) -3A Last play time in time_t (6 bytes) -40 Total playtime in time_t (4 bytes) // will record 136.1 years of playtime - -44 SHA-256 hash of worldinfo1 (32 bytes) -72 SHA-256 hash of worldinfo2 (32 bytes) -A4 SHA-256 hash of worldinfo3 (32 bytes) - -D6 Uncompressed size (2 bytes) -D8 Deflated thumbnail image in TGA format -p-2 (it's deflated so that it saves faster, so no Lzma) -p-2 0xFF -p-1 0xFE - -Note: if you're going to add more footer beyond this point, DON'T; - instead pack the thumbnail.tga and other footers in TEVD container. \ No newline at end of file diff --git a/work_files/DataFormats/Worldmap format.txt b/work_files/DataFormats/Worldmap format.txt deleted file mode 100644 index ff70323af..000000000 --- a/work_files/DataFormats/Worldmap format.txt +++ /dev/null @@ -1,94 +0,0 @@ -Terrarum Game Map Format - -* Endianness: LITTLE - -Ord Hex Description -00 54 T -01 45 E -02 4D M -03 7A z # 'z' because it's compressed - -04 03 Version revision number of this format (unreleased numbers also count) - -05 03 Number of layers, NOT the number of payload - -06 05 Number of payloads - -07 01 Compression algorithm, 0 for none, 1 for DEFLATE, 2 for LZMA, otherwise undefined (maybe LZMA2 for the future?) - Value of 01 (DEFLATE) is recommended for its faster compression - -08 World generator version. If the generator adds new feature (e.g. new ores, new buildings) -09 this number must be incremented by one. - -0A World width -0B World width -0C World width -0D World width - -0E World height -0F World height -10 World height -11 World height - -12 Default spawn coord in Absolute Tile Number -13 Default spawn coord in Absolute Tile Number -14 Default spawn coord in Absolute Tile Number -15 Default spawn coord in Absolute Tile Number -16 Default spawn coord in Absolute Tile Number -17 Default spawn coord in Absolute Tile Number - -# Payload -# -# Each layer and other information are stored as a "payload" -# A payload is consisted as follows: -# -# Literal Description -# "\0pLd" Payload header [00, 70, 4C, 64] -# [4] Identifier. 4 lettres ASCII string -# [6] Uncompressed size of DEFLATEd binary (max size 256 TB) -# [6] Length of the actual payload (max size 256 TB) -# [..] DEFLATEd binary (begins with one of these: 0x789C, 0x78DA, 0x7801) - - -Payload "TERR" -- world terrain data in Uint16 - Uncompressed size will be 2x of (width * height) - -Payload "WALL" -- world walls data in Uint16 - Uncompressed size will be 2x of (width * height) - -Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float32 damage) - Uncompressed size will be arbitrary (multiple of tens) - -Payload "WdMG" -- world walls damage data, array of: (Int48 tileAddress, Float32 damage) - Uncompressed size will be arbitrary (multiple of tens) - -Payload "FlTP" -- world fluid types, array of: (Int48 tileAddress, Signed Int16 type) - Uncompressed size will be arbitrary (multiple of eights) - -Payload "FlFL" -- world fluid fills, array of: (Int48 tileAddress, Float32 amount) - Uncompressed size will be arbitrary (multiple of tens) - If the 'amount' < 0.0001f (WorldSimulator.FLUID_MIN_MASS), the entry must be discarded - -Payload "WiNt" -- wiring nodes, in JSON format - -Payload "TMaP" -- tile number to name map, array of: (Int32, tileNumber, String itemID) - String is null-terminated byte array - -TODO need a format that can store arbitrary number of conduits, not just limited to 32 - -/*Payload "CtYP" -- conduit types, array of: (Int48 tileAddress, Uint32 bitarray) - can hold 32 different wires simultaneously - -Payload "CfL0" -- conduit fills, aka size of liquid/gas packet, array of: (Int48 tileAddress, Float32 fill) - CfL0..CfL9, CfLa..CfLf are available to store values for 16 different things.*/ - - -EOF 45 E -EOF 6E n -EOF 64 d -EOF 54 T -EOF 45 E -EOF 4D M -EOF FF Byte order mark -EOF FE Byte order mark - diff --git a/work_files/DataFormats/just-json-it-saveformat.md b/work_files/DataFormats/just-json-it-saveformat.md deleted file mode 100644 index 5f7d9108b..000000000 --- a/work_files/DataFormats/just-json-it-saveformat.md +++ /dev/null @@ -1,127 +0,0 @@ -## Savegame Structure - -- The Savegame is a TerranVirtualDisk archive that stores multiple files in the disk's root directory -- Savegame stores metadata, Worlds and Actors in the game -- A player gets one unique Savegame -- A player can have Multiple worlds - - Worlds are identified using integer ranged 1 through 32767 (inclusive) -- Actor ID is unique within the scope of the Savegame - - A World stores list of Actor IDs that resides in the world - - -### File Structure - -Each file on the Savegame has following convention: - -|Type|Filename|ID| -|---|---|---| -|Metadata|savegame|-1| -|Blocks Properties|blocks|-16| -|Items Properties|items|-17| -|Wires Properties|wires|-18| -|Materials Properties|materials|-19| -|Factions Properties|factions|-20| -|Other Properties used by modules|modprops|-1024| -|Worlds|world$n ($n is a world index)|$n| -|Actors|actor$n ($n is an Actor ID)|$n| - -User formats can have ID of -2147483648..-65536 - -### Solving Problems - -#### How do I determine which world to read in? - -Load the player (always has the entry ID of 9545698) and the property "worldCurrentlyPlaying" should -contain an integer that is a world index. Only the actors that are instance of IngamePlayer will have -the property. - - -### Save File Examples - -Following code is an example Savegame JSON files. - -#### savegame.json -``` -{ - savename: "Test World 1", - genver: 0x00030001, /* generator version in integer; always use TerrarumAppConfiguration.VERSION_RAW */ - terrseed: "84088805e145b555", - randseed: "19b25856e1c150ca834cffc8b59b23ad", - weatseed: "e5e72beb4e3c6926d3dc9e3e2ef7833b", - playerid: 9545698, - creation_t: , - lastplay_t: , - playtime_t: , - thumb: , - loadorder: , - worlds: [1,2,6,7] -} -``` - -#### world1.json - -File is named as `"world"+world_index+".json"`. -The fields are auto-generated by GDX's JSON serialiser. - -``` -{ - worldName: "New World", - worldIndex: 1, - width: 9000, - height: 2250, - spawnX: 4500, - spawnY: 248, - creationTime: 1629857065, - lastPlayTime: 1629857065, - totalPlayTime: 0, - layerTerrain: { - h: , - b: , - x: 9000, - y: 2250 - }, - layerWall: { - h: , - b: , - x: 9000, - y: 2250 - }, - wallDamages:{}, - terrainDamages: {}, - fluidTypes: {} - fluidFills: {}, - wirings: {}, - wiringGraph: {}, - gravitation: {y:9.8} - globalLight: { - r:0.8826928, - g:0.8901961, - b:0.9055425, - a:0.93691504 - }, - averageTemperature: 288, - generatorSeed: 0, - worldTime: 27874, - tileNumberToNameMap: {}, - extraFields: {}, - genver: 4 - comp: 1 -} -``` - -#### actors.json - -The fields are auto-generated by GDX's JSON serialiser. - -``` -[ - { /* actor serialised in JSON * - class: "net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer", /* depends on the actor */ - referenceID: 1342111743, - actorValue: { /* actorValue serialised in JSON */ }, - hitbox: ..., - ... - }, - ... -] -``` \ No newline at end of file diff --git a/work_files/DataFormats/terrarum_advanced_lang_file.txt b/work_files/DataFormats/terrarum_advanced_lang_file.txt new file mode 100644 index 000000000..fa8bde7d5 --- /dev/null +++ b/work_files/DataFormats/terrarum_advanced_lang_file.txt @@ -0,0 +1,47 @@ + +# example lang file + +meta language:fiFI +meta nounclasslabel:nom,par,accnom,accgen,gen,iness,ela,illa,adess,abla,alla,ess,trans,inst,abess,comit,pnom,ppar,paccnom,paccgen,pgen,piness,pela,pilla,padess,pabla,palla,pess,ptrans,pinst,pabess,pcomit +meta nounseries:follow-nounclasslabel # basically tells the parser that 'nounclasslabel' has everything you need +CONTERT_HOUSE_NOUN:talo,taloa,talo,talon,talon,talossa,talosta,taloon,talolla,talolta,talolle,talona,taloksi,,talotta,,talot,taloja,talot,talot,talojen,taloissa,taloista,taloihin,taloilla,taloilta,taloille,taloina,taloiksi,taloin,taloitta,taloineen +CONTEXT_GO_TO_VERB:mennä <1:illa> # with CONTERT_HOUSE_NOUN: "mennä taloon" + + +meta language:koKR +meta nounclasslabel:use korean # built-in automation for korean +meta nounseries:undefined # the grammar of this language does not take noun's count into account +CONTEXT_HOUSE_NOUN:집 +CONTEXT_TOWARDS_VERB:<1>{1:로,으로,로} 가기 # when 'korean' is used for nounclasslabel, the character code of the hangul letter is taken into account. GIVEN_WORD_SET.get(i), where i = ((char - 44032) % 28 == 0) ? 0 : ((char - 44032) % 28 == 8) 2 : 1 + + +meta language:enUS +meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural +CONTEXT_HOUSE_NOUN:House,Houses +CONTEXT_TOWARDS_VERB:Go to <1> + + +meta language:frFR +meta nounclasslabel:m,f,pm,pf,vm,vf +meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural +CONTEXT_COVID_NOUN:f:Covid,Covid +CONTEXT_INTERNATIONALE_NOUN:vf:Internationale,Internationale +CONTEXT_THE_STH:{1:Le ,La ,L’,L’}<1> # with CONTEXT_COVID_NOUN: "La Covid"; with CONTEXT_INTERNATIONALE_NOUN: "L’Internationale" + + +meta language:la +meta nounclasslabel:m,f,n +meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural +CONTEXT_CHICKEN_NOUN:m:Gallus,Galli +CONTEXT_VERITAS_NOUN:f:Veritas,Veritates +CONTEXT_X_IS_MY_LIGHT:<1:plural> lux {1:meus,mea,meum} # 'plural' is pre-defined name that comes with 'singular-plural' nounseries + + +## the preamble + +meta nounclasslabel:m,f,n,mp,fp,np # for german: masculine singular, feminine singular, neuter singular, masculine plural, feminine plural, neuter plural + +meta nounclasslabel:m,f,n,md,fd,nd,mp,fp,np # for sanskrit: masc./fem./neu. singular, dual and plural + +meta nounclasslabel:use korean # a pragma to use built-in automation labeled 'korean' +