Files
sessionzero/szds.md

312 lines
9.4 KiB
Markdown

# SessionZero Data System
The formula system is not implemented yet. The `formula` type is reserved in the type system, but it does nothing right now. Formula fields resolve to `0` until the formula system exists.
## Layout
Data is separated into 3 main chunks: systems, games, and datasets. Each of these work together but hold varying information.
A system owns its datasets. A dataset always belongs to a system, even if the dataset folder gets pulled out, archived, and installed somewhere else later.
A game installs exactly one system. That system's datasets are what get installed into the game.
The flow goes as such:
1. Create a System
- Define a character template
- Define data templates
2. Create a Dataset (owned by a System)
- Reference the owning system for available templates
- Create data instances based on the available templates
3. Create a Game
- Install a System
- Install the datasets that belong to that system
- Install plugins (future feature, not covered in this spec)
- Create a character from the system's character template
## File Specifications
### Namespaces and IDs
There are 3 "namespaces" in the sz data pipeline: system, dataset, and data group in that order.
Author is just metadata, so it's not part of the ID chain. Author lives as a field inside `system.sz` and `dataset.sz`.
Namespaces are accessed with a trailing `:`, ex. the weapon data group inside the `szcore_data` dataset inside the `szcore` system is `szcore:szcore_data:weapon`.
IDs for top level files (`system.sz`, `dataset.sz`) come from the `id` field declared inside that file, not the folder name. Folder naming (`author.system_name/`) is a human-readable convention for organizing files on disk, it has no effect on ID resolution.
`.szt` and `.szo` files are identified by their file name.
Scope rules:
- A `system_id` is unique across everything installed locally.
- A `dataset_id` is unique within its owning system. Two different systems can each have a dataset called `homebrew` with no conflict, since the system segment disambiguates them.
- A `data group` (folder name/template name) is unique within its system (for templates) or within its dataset (for data, since it mirrors the data group it belongs to).
Full resolution path example:
A data instance in the `szcore_data` dataset (owned by the `szcore` system), inside the `weapon/` folder, named `sword.szo`, resolves as:
`szcore:szcore_data:weapon:sword`
`dataset.sz` carries a `system_version` field. It pins the dataset to a specific system version. It's not enforced yet, but it's there so datasets authored now don't end up missing a version pin once versioning is enforced.
Systems and datasets only exist inside a game right now. A local library that holds systems and datasets outside of any specific game (so they don't need to be duplicated across games) is planned, but not part of this spec yet. When it's added, it will keep the same `system_id:dataset_id` shape, so nothing needs to be re-identified when it moves from library to installed-in-a-game.
### Fields
A field is found within a data object, defined by a template.
Types: `text`, `number`, `boolean`, `option`, `list`, `formula`.
Every type has an enforced default value if one isn't set on the template. If the template author doesn't set `default_value`, it falls back to the type's zero-value:
- `text` -> `""`
- `number` -> `0`
- `boolean` -> `false`
- `option` -> unset / no selection
- `list` -> empty list
`formula` fields cannot have a `default_value`, since they're computed, not stored. They resolve to `0` until the formula system is implemented.
### Resolution & Validation Behavior
Rules for what happens when a dataset's data doesn't cleanly match the currently installed system's templates.
- **data group has no matching template in the system** (ex. a dataset has an `armor/` folder but the installed system has no `armor.szt`): a warning is logged, and that group's data is skipped.
- **Instance has a field the template no longer defines**: the field is stripped on load.
- **Template has a required field the instance doesn't define** (ex. a field got added to the template after the instance was authored): the default value is loaded in at runtime. This does not write back into the `.szo` file, the file on disk stays as-is until something explicitly saves it.
## System
Holds:
- Character Template
- Data templates
The character template is different from a regular data template. There's only ever one per system, it lives at the system root (not inside `templates/`), and datasets cannot use it. It's only ever used to create a character inside a game.
Example Directory Structure:
```text
author.system_name/
- system.sz
- icon.png
- character_template.szt
- templates/
- weapon.szt
```
Example `system.sz` file
```toml
id = "szcore"
display_name = "SZ Core System"
version = "1.0"
author = "sz"
description = """
A simple core system to base others off of
"""
```
Example `character_template.szt` file
```toml
template_type = "character"
author = "sz"
[fields.name]
type = "text"
required = true
default_value = "Character 1"
[fields.player_name]
type = "text"
required = false
[fields.level]
type = "number"
required = true
default_value = 1
[fields.race]
type = "option"
required = true
option_type = "data"
data_source = ["races"]
[fields.class]
type = "option"
option_type = "text"
data_source = ["ranger", "fighter", "wizard"]
required = true
[fields.status.is_dead]
type = "boolean"
required = true
default_value = false
[fields.stats.str]
type = "number"
required = true
default_value = 0
[fields.stats.modifiers.str]
type = "formula"
formula = '(stats.str - 10) / 2'
[fields.inventory]
type = "list"
list_type = "data" # if a list type is `data`, data_source becomes a list of data groups the list can pull from. otherwise use `number` or `text` for literal values
data_source = ["items", "weapons"]
[fields.spells]
type = "list"
list_type = "text"
data_source = ["fireball", "mage_hand"]
[fields.equipped_weapon]
type = "option"
option_type = "data"
data_source = ["weapons"]
[fields.stats.damage] # NOTE: Formulas are not implemented yet so this will resolve to a 0
type = "formula"
formula = 'equipped_weapon.calculated_damage'
```
Example data template file `weapon.szt`
```toml
# The data type eg. "item" is pulled from the file name, and can be anything
template_type = "data"
author = "sz"
[fields.id]
type = "text"
required = true
[fields.display_name]
type = "text"
required = true
[fields.value]
type = "number"
required = true
default_value = 1
[fields.base_damage]
type = "number"
required = true
default_value = 1
[fields.skill]
type = "text"
required = true
default_value = "str"
[fields.calculated_damage] # NOTE: Formulas are not implemented yet so this will resolve to a 0
type = "formula"
formula = 'base_damage + $PLAYER.stats.modifiers.{skill}'
# TODO: syntax for this still needs to be figured out.
# $PLAYER is a special variable that gets the current character of the owning player within a session.
# the local `skill` variable needs to be evaluated as a string value (so it looks for `$PLAYER.stats.modifiers.str` as a number).
# not sure about the {} syntax, may need to rework the formula type entirely.
```
## Datasets
Holds:
- Reference to the owning System and its templates
- Data Instances
Example Directory structure
```text
author.dataset_name/
- dataset.sz
- icons/
- dataset.png
- weapon/
- sword.png
- data/
- weapon/
- sword.szo
```
Example `dataset.sz` file
```toml
id = "szcore_data"
system_id = "szcore"
system_version = "1.0"
author = "sz"
```
Example data instance file `sword.szo` (uses the `weapon` template from the owning system)
```toml
author = "sz"
[fields.id]
value = "sword"
[fields.display_name]
value = "Sword"
[fields.value]
value = 100
[fields.base_damage]
value = 10
[fields.skill]
value = "str"
# this field is calculated based on the template's formula. resolves to 0 until the formula system exists.
[fields.calculated_damage]
value = 0
```
## Games
The Game type glues everything together. A game installs exactly one system. Datasets live nested under that system, since they can't exist independent of it.
Holds:
- System
- Datasets
- Plugins
- Player Data
- Character Instances
- Gamestate/Data
Example directory structure:
```text
game_name/
- game.sz
- system/
- system.sz
- datasets/
- dataset_a/
- dataset_b/
- plugins/
- (plugins will be defined in the future)
- player_data/
- player_name/
- player.sz
- characters/
- character1.szo // An actual Character instance using the System's template
- game_data/
- (various game related data...)
```
Example `game.sz` file
```toml
id = "my_game"
owner = "my_username" # The username that owns this game instance, used for online games in the future but does nothing yet.
system_id = "szcore"
system_version = "1.0" # Does nothing yet.
game_type = "local" # Does nothing yet, but the options are: local, online.
```
## Formula System
Not implemented yet. The `formula` type exists in the type system and templates can be authored with formula fields, but nothing evaluates them right now, they resolve to `0`.
Todo list for formulas:
- Scope model: what variables are reachable inside a formula (`self`, `$PLAYER`, others?)
- Grammar: real expression parser vs simple dot-path substitution
- Cycle detection between dependent formula fields
- The `$PLAYER.stats.modifiers.{skill}` interpolation syntax used in `weapon.szt` above