Database
The game stores all data in a save folder with RocksDB database. This does not include data such as assets or Lua files. A single "save" is defined as an entire RocksDB database folder.
What does the database store?
- Galaxy systems, sectors, planets
- Player information
- Entity locations (not realtime)
- Inventory (cargo)
- Ships
- Currencies
- Anything that we need to load that is not an asset
Why a database?
Why do we need a database in a game?
The universe is persistent, and we need to store the data somewhere. When you warp out of a sector the state of that sector is saved. When you warp back to that sector the state is loaded and restored back.
There are many ways how a game can store a data. Many games use their own formats. We can use JSON files. We can use SQL. There are infinite options.
The Temporary Escape uses a document database because of the following reasons:
- Document store database does not need a strict schema, which allows for easier modding.
- We can store small and large data very efficiently.
- It's very fast.
Where is the database stored?
| Platform | Path |
|---|---|
| Windows | C:\Users\YOUR_USERNAME\AppData\Roaming\Temporary Escape\Saves\SAVE_NAME\ |
| Linux | ~/.temporary-escape/Saves/SAVE_NAME/ |
| MacOS | ~/Library/Application Support/Temporary Escape/Saves/SAVE_NAME/ |
Document style
The RocksDB database is a key-value database but has been modified to work as a document database with indexes. This is a custom made database logic that is part of the game engine. You can think of a document as a collection of some values with keys that have values. Such values can have nested documents.
A simplified example of a galaxy system represented as JSON would be:
json
{
"name": "Intra",
"pos": [
83.75674,
-23.2574
],
"seed": 125596117633147,
"galaxyId": 1,
"regionId": 7,
"factionId": null
}INFO
Please note that documents are not stored as a JSON in the database, instead they use CBOR serialization.
Document names, keys, and indexes
A document is a key-valur pair where the key is an auto-incrementing integer number. The document key can never be zero!
WARNING
Always assume that a key of value zero means that the key points to a non-existing document! A valid key is never zero.
The value can be anything. In most cases it is an object (a Lua table). However, a value can be even a string.
A document also has indexes. The indexes are managed automatically. You do not need to create them nor delete them. All you have to do is to define indexes. Indexes are defined by a schema, more on that in a section below: Schemas.
Accessing via Lua scripts
To access the database (read, modify, write, delete) you can do so through any Lua script.
All data in the database is accessed via Lua types such as tables, strings, numbers, etc. Never classes.
The database instance can be accessed by engine.db inside any Lua script. The db is a member of engine, and the engine is globally defined module. Also note that any operation on such db database instance is done via self, therefore you need to call functions via the : syntax!
Database operations
There are many database operations. The basic are:
You can also iterate over
Insert a new document
The following example below inserts a new document of name Example and prints the unique ID of the document. An insert always produces a new document ID!
lua
-- `engine` is globally defined! and `engine.db` is a database instance!
local id = engine.db:insert("Example", {
foo = "hello",
bar = 42,
nested = {
a = true,
b = nil,
},
})
logger:info("Inserted example id: %d", id)Using non-table types is also valid, example below:
lua
local id0 = engine.db:insert("Example", "Hello World!")
local id1 = engine.db:insert("Example", true)
local id2 = engine.db:insert("Example", nil)Get a document
Getting a document is done by its numerical ID.
If the document does not exist then a nil is returned.
lua
local id = engine.db:insert("Example", {
msg = "Hello World!"
})
local doc = engine.db:get("Example", id)
logger:info("Document msg: %s", doc.msg)
-- Prints: "Document msg: Hello World!"Delete a document
Deleting a document is similar as getting it. You use the document numerical ID.
Deleting a document that does not exist produces NO error. This also means that deleting the same document multiple times is valid (the document is deleted only once).
lua
local id = engine.db:insert("Example", {
msg = "Hello World!"
})
engine.db:remove("Example", id)
local doc = engine.db:get("Example", id)
logger:info("Document is nil: %s", doc == nil)
-- Prints: "Document is nil: true"Update an existing document
Updating an existing document can be done in tree ways. Replacing the entire value, using a callback function, or using a transaction. In here we show only the first two options. On the third method see Transactions section.
Update by replacing the value works similarly as an insert, except you also need to provide the document key.
lua
local id = engine.db:insert("Example", {
msg = "Hello World!"
})
engine.db:update("Example", id, {
msg = "Updated Message!"
})
local doc = engine.db:get("Example", id)
logger:info("Document msg: %s", doc.msg)
-- Prints: "Document msg: Updated Message!"The second option is using the callback function. The function must accept a single argument, which is the existing document value that needs to be updated. The function also must return the updated document. It does not matter if you update the old value and return it, or create a completely new value, it's up to your preference. Raising an error inside the callback function will cancel the update and will forward the error up.
lua
local id = engine.db:insert("Example", {
msg = "Hello World!"
})
engine.db:update("Example", id, function (doc)
doc.msg = "Updated Message!"
return doc
end)Upsert (insert or update)
Seek (iterating multiple keys)
Database indexes
Seek by index
Transactions
Example: explore a system
Database in details (optional)
TIP
This is an in-depth explanation of how the database is organized internally. You do not need to understand this part in order to create mods!
Keys
Keys are not stored in the document. Keys are stored only by the database with the key-value pairs.
Example of a key:
docs:Systems:{id}The key starts with a 4-character prefix. The docs means that the value is a document. The : is used as a separator. The System is the name of the document (you could think of it as a namespace, or a table name).
And lastly, the {id} is an 64-bit integer in BIG endian order. This is a unique ID of the document. However, differently named documents can share the same ID. The ID is only unique between the same document names.
CBOR format
Timestamps
Timestamps are represented via tag 1001 and a map of 3 int keys with uint values (extended time as in RFC9581). Such as:
text
1001({
1: <uint> ; POSIX epoch in seconds
-9: <uint> ; nanoseconds fraction part
-13: <uint> ; timescale, always 0 (UTC with POSIX epoch)
})Example of 1745966486998652396ns [2025-04-29 22:41:26 UTC] would be:
text
1001({
1: 1745966486
-9: 998652396
-13: 0
})Timestamps are always presented in Lua as a TimePoint type.
