adding loadorder to savegame

This commit is contained in:
minjaesong
2022-08-31 02:40:46 +09:00
parent 0310c71c74
commit e4caf29791
11 changed files with 95 additions and 321 deletions

View File

@@ -0,0 +1,82 @@
## Introduction
On the main game, any player can access any generated worlds, and thus players data and worlds are saved separately.
The main game directory is composed of following directories:
```
.Terrarum
+ Players
- "${PlayerName}-${UUID}", TVDA {
[-1] player JSON,
[-2] spritedef,
[-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
*disk name is player's name encoded in UTF-8
+ Shared
- <e.g. Disk GUID>, TEVD { * }
- <this directory can have anything>
+ Worlds
- "${WorldName}-${UUID}", TVDA {
[-1] world JSON with Player Data,
[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
```
(TEVD stands for Terrarum Virtual Disk spec version 3, TVDA stands for spec version 254; both have MAGIC header of `TEVd`)
Do not rely on filename to look for a world; players can change the filename
## Handling The Player Data
Some of the "player assets" are stored to the world, such assets include:
- Physical Status (last position and size as in scale)
- Inventory (instance of ActorInventory)
- Actorvalues (only on Multiplayer)
### Loading Procedure
1. Load the Actor completely first
2. Load the World
3. Overwrite player data with the World's
If the World has the Actorvalue, World's value will be used; otherwise use incoming Player's
Multiplayer world will initialise Actorvalue pool using incoming value -- or they may choose to use
their own Actorvalue called "gamerules" to either implement their own "gamemode" or prevent cheating)
For Singleplayer, only the xy-position is saved to the world for later load.
Worlds must overwrite new Actor's position to make them spawn in right place.
### Remarks
Making `inventory` transient is impossible as it would render Storage Chests unusable.
## Prerequisites
1. Player ID must not be strictly 9545698 (0x91A7E2)
1. Use classname `net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer` to check
2. Each World and Player has to be uniquely identifiable via GUID
3. `ActorNowPlaying` must be drawn on top of other actors of same RenderOrder
## To-dos After the Initial Implementation
1. Modify Savegame Crackers and Disk Crackers to work with the new scheme
2. Create Player Creator Tool for avatar-makers
## Goals
1. Allow multiple players share the same world
2. Make multiplayer possible
3. Make Players distributable (like VRChat avatars)

View File

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

View File

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

View File

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

View File

@@ -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: <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>,
loadorder: <LoadOrder serialised>,
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: <SHA-256 hash of 'b'>,
b: <Ascii85-encoded gzipped terrain layerdata>,
x: 9000,
y: 2250
},
layerWall: {
h: <SHA-256 hash of 'b'>,
b: <Ascii85-encoded gzipped wall layerdata>,
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: ...,
...
},
...
]
```

View File

@@ -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: "LInternationale"
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'