SessionZero-Client/sessionzero-design-doc-wip.md
2025-10-17 23:38:04 -05:00

18 KiB

SessionZero Design Document

1. Introduction

1.1 Purpose

SessionZero is a free and open-source TTRPG companion app designed to streamline tabletop role-playing game management. It emphasizes flexibility, modularity, and federation through a system-agnostic datapack format, supporting both offline and self-hostable online play. The goal is to empower players and GMs with full control over their data, rulesets, and session management.

1.2 Scope

The project will begin as a cross-platform desktop application (Windows and Linux) built primarily with Godot/GDScript. This client application can also function as the Host Authority (server) for a session. The architecture is decentralized: user accounts are replaced with a cryptographic key-pair identity model, and all session data is stored locally on the GM's machine. The centralized content database (SessionZeroDB) is decoupled and planned as a separate, federated content indexing project in a later phase.

1.3 Objectives

  • Provide a modular, open-source companion app for any TTRPG system.
  • Implement a JSON-based datapack format for extensible content storage.
  • Support full offline use and resilient, self-hostable online sessions.
  • Implement a decentralized, token-based identity system.
  • Create a foundation for future content federation and mobile support.

2. System Overview

SessionZero operates with three main layers designed for independence and resilience:

  1. Frontend (Client/Host Authority): The Godot application running on every machine. It serves as the primary UI, the Datapack consumer, and, when hosting a game, acts as the authoritative session server utilizing Godot's High-Level Multiplayer (HLM) system.
  2. Backend (Optional Signaling Service): A minimal, open-source Minimal Signaling Server (MSS) available for easy peer discovery. This service handles ephemeral connection brokering but stores no game data or persistent identity information. This layer is completely optional, as users can establish direct P2P connections without any signaling service.
  3. Datapack System: A structured archive format (.szpack) for content, with a fully decoupled content federation strategy (SessionZeroDB) planned for later phases.

3. Technology Stack

  • Client/Host: Godot (cross-platform desktop) and GDScript. Godot HLM is used for all session communication (P2P/UDP).
  • Signaling Server: Go (Golang) for the minimal, high-performance MSS (Minimal Signaling Server).
  • Database/Persistence: Local file storage, Godot Resources, and SQLite for saving session state, character instances, and the Host's player identity data.
  • Networking: Hybrid P2P (UDP Hole Punching or UPnP fallback) or a direct P2P (UPnP/port-forward) option.

4. Core Data Concepts

4.1 Datapack

The top-level container (.szp file), bundling all system-specific data (templates, datasets, assets).

4.2 Template

A reusable definition of a data structure (e.g., a Character Template defining character sheet fields, or a Session Template defining campaign structure).

4.3 Dataset

A structured collection of external data records (e.g., a bestiary, a list of items, or spells).

4.4 Instance

The runtime data created from a Template (e.g., "Aelric's Character Sheet" based on the "Fantasy Character" Template).

4.5 Schema

Formal JSON Schemas used to validate the structure of Datapacks and their contents.

5. Datapack Specification

A datapack is a self-contained archive (.szp) that holds all data and assets for a specific TTRPG system, module, or expansion. It serves as the foundation for SessionZero's extensible content model. Each pack can define datasets, templates, and optionally contain instance data (though instances are usually stored separately as save files). The structure emphasizes modularity, portability, and reliable dependency management between packs.

5.1 Directory Structure

pack-name/
├── szpack.json                 # Pack metadata and manifest
├── media/
│   └── images/                 # Image assets (referenced by objects)
└── objects/
    ├── datasets/               # User-defined structured data (e.g. items, NPCs)
    ├── character-templates/    # Templates defining character structures
    └── session-templates/      # Templates defining session/campaign structures

5.2 Pack Metadata (szpack.json)

Each datapack contains a root metadata file that defines its identity, authorship, dependencies, and compatibility information. This structure aligns with the C# Datapack and DatapackDependency models.

{
  "id": "c9a3f9c8-8b8a-4f1f-9f3b-2d8c7f2c6c62",
  "name": "Example Pack",
  "version": "1.0.0",
  "author": "UserName",
  "license": "CC-BY-SA-4.0",
  "description": "A fantasy setting datapack containing items and NPCs.",
  "createdAt": "2025-10-15T00:00:00Z",
  "sessionZeroVersion": "1.0.0",
  "dependencies": [
    {
      "id": "c2e6d0a4-2a8f-4a51-b36e-9f8f2c7b1d11",
      "name": "Core-Ruleset",
      "version": "1.2.0"
    }
  ]
}

5.3 Common Object Metadata

Objects within the datapack (datasets, templates) inherit from SzObject. The szType field is required to identify the object type. Image paths are relative to the media/images folder.

{
  "id": "core-items",
  "name": "Core Items",
  "szType": "dataset",
  "description": "A collection of basic weapons and armor.",
  "icon": "core-items.png",
  "version": "1.0.0",
  "schemaVersion": "1.0.0"
}

Note: The system resolves the icon field path (e.g., "core-items.png") to datapack/media/images/core-items.png.

5.4 Dataset Objects

Datasets are structured collections of data entries. The entry structure includes TopLevelFields and optional Groups of fields, aligning with the DatasetEntry C# model.

{
  "id": "core-items",
  "szType": "dataset",
  "datasetType": "items",
  "name": "Core Items",
  "description": "Weapons, armor, and consumables for the base ruleset.",
  "version": "1.0.0",
  "schemaVersion": "1.0.0",
  "entries": {
    "sword": {
      "id": "sword",
      "name": "Sword",
      "description": "A basic weapon.",
      "icon": "weapons/sword.png",
      "topLevelFields": {
        "damage": { "type": "Number", "value": 10 },
        "weight": { "type": "Number", "value": 3 }
      },
      "groups": [
        {
          "id": "stats",
          "name": "Stats",
          "fields": {
            "rarity": { "type": "Text", "value": "Common" }
          }
        }
      ]
    },
    "potion": {
      "id": "potion",
      "name": "Healing Potion",
      "description": "Restores a small amount of HP.",
      "icon": "consumables/potion.png",
      "topLevelFields": {
        "healAmount": { "type": "Number", "value": 20 },
        "consumable": { "type": "Boolean", "value": true }
      }
    }
  }
}

Note: The field retrieval path is expected to be EntryName.FieldName or EntryName.GroupName.FieldName, matching the logic in Dataset.cs.

5.5 Character Templates

Character templates define the fields and structure for characters. Note the use of the specific DatasetLink object for list fields, requiring a Datapack ID, Dataset ID, and Version.

{
  "id": "default-character",
  "szType": "character-template",
  "name": "Generic Character",
  "description": "A base character layout usable across multiple systems.",
  "icon": "character.png",
  "version": "1.0.0",
  "schemaVersion": "1.0.0",
  "sections": [
    {
      "id": "core",
      "name": "Core Stats",
      "groups": [
        {
          "id": "attributes",
          "name": "Attributes",
          "fields": [
            { "id": "strength", "name": "Strength", "type": "Number", "defaultValue": 10 },
            { "id": "dexterity", "name": "Dexterity", "type": "Number", "defaultValue": 10 },
            { "id": "intelligence", "name": "Intelligence", "type": "Number", "defaultValue": 10 }
          ]
        },
        {
          "id": "inventory",
          "name": "Inventory",
          "fields": [
            {
              "id": "equipment",
              "name": "Equipment",
              "type": "List",
              "datasetLink": {
                "datapackId": "c9a3f9c8-8b8a-4f1f-9f3b-2d8c7f2c6c62",
                "datasetId": "core-items",
                "version": "1.0.0"
              }
            }
          ]
        }
      ]
    }
  ]
}

5.6 Session Templates

Session templates are blueprints for setting up a campaign. This example uses CharacterTemplateLink and a list of DatasetLink objects for RequiredDatasets as defined in SessionTemplate.cs.

{
  "id": "basic-session",
  "szType": "session-template",
  "name": "Basic Fantasy Campaign",
  "description": "A classic campaign setup with standard fantasy rules.",
  "icon": "session.png",
  "version": "1.0.0",
  "schemaVersion": "1.0.0",
  "characterTemplateLink": {
    "datapackId": "c9a3f9c8-8b8a-4f1f-9f3b-2d8c7f2c6c62",
    "templateId": "default-character",
    "version": "1.0.0"
  },
  "requiredDatasets": [
    {
      "datapackId": "c9a3f9c8-8b8a-4f1f-9f3b-2d8c7f2c6c62",
      "datasetId": "core-items",
      "version": "1.0.0"
    }
  ],
  "sections": [
    {
      "id": "session-info",
      "name": "Session Info",
      "groups": [
        {
          "id": "details",
          "name": "Details",
          "fields": [
            { "id": "setting", "name": "Setting", "type": "Text", "defaultValue": "Thalindra" },
            { "id": "gmNotes", "name": "GM Notes", "type": "MultiText" }
          ]
        }
      ]
    }
  ]
}

5.7 Instances

Instances, such as a specific character or a running campaign's state, are stored outside of the core, reusable datapacks and are instead stored in the local db/filesystem.

5.8 Validation and Schema Versioning

Formal, versioned JSON Schemas define the structure of szpack.json and core object types.

  • Each object declares a schemaVersion (global versioning per SessionZero release).
  • Validation tools confirm compliance before import/export.
  • Internal references (DatasetLink, CharacterTemplateLink, etc.) are resolved within the pack automatically.
  • External references are verified via dependencies in szpack.json.

Session Datapack vs Templates:

  • Session Template: A reusable blueprint for campaign/session setup. Lives under objects/session-templates/.
  • Session Datapack: The persisted, evolving state of an ongoing campaign. It is stored as a special pack and does not appear alongside normal content packs in the UI. Snapshots are versioned with timestamps. Export/import uses the same .szp format with a distinct type flag in szpack.json.

6. Networking & Synchronization

6.1 Session Host Authority

A "Session" is a single game instance, always hosted by one GM whose client is running as the Host Authority. The Host is responsible for state validation, conflict resolution, and persistence to its local storage. All connected clients sync their state with this central Host.

6.2 Dual Connection System

SessionZero provides two independent connection methods, giving users full control over how they establish multiplayer sessions. Both methods use P2P connections with automatic fallback mechanisms, ensuring the application remains functional regardless of network configuration.

6.2.1 Signaling Server Connection (MSS Method)

The first connection method uses a Minimal Signaling Server (MSS) to facilitate peer discovery and connection establishment:

Process Flow:

  1. The Godot Host generates a short, temporary Session Key (e.g., ALPHA-2345)
  2. The Host determines its external connection addresses (UDP/WS) and registers them with a configured MSS
  3. Players enter the Session Key in their client to retrieve the Host's connection information
  4. Connection is attempted using UDP Hole Punching for direct P2P communication
  5. If UDP Hole Punching fails, the client automatically falls back to:
    • UPnP (Universal Plug and Play) if the router supports it
    • Direct connection if the port is already forwarded
  6. If all connection methods fail, the player is notified and cannot join

MSS Configuration:

  • Users can select which MSS to use in application settings
  • An official MSS instance will be provided by the project
  • The MSS source code and binaries are distributed for self-hosting
  • The MSS is ephemeral and stateless, immediately deleting Session Keys after lookup

Use Case: This method is ideal for users who want easy session setup without manual network configuration, while still maintaining the option for self-hosted infrastructure.

6.2.2 Direct Connection Method

The second connection method completely bypasses the MSS layer, establishing direct P2P connections using the Host's IP address or domain name:

Process Flow:

  1. The GM provides players with their public IP address, domain name, or tunnel endpoint (e.g., ngrok, Cloudflare Tunnel)
  2. Players enter this address directly into their client
  3. Connection is attempted using:
    • UPnP (Universal Plug and Play) if the router supports it
    • Direct connection if the port is already forwarded
  4. If neither method succeeds, the player is notified and cannot join

Requirements:

  • The GM must have either:
    • A router with working UPnP enabled
    • Manually forwarded ports on their router
    • A tunneling service configured (ngrok, Cloudflare Tunnel, etc.)

Use Case: This method is crucial for users who prefer complete independence from any third-party services (including self-hosted MSS instances), have their own networking infrastructure, or simply want the most direct connection path possible.

6.3 Connection Method Independence

These two connection methods are completely independent of each other:

  • The Direct Connection method is not a fallback for MSS connection failures
  • Users choose their preferred method based on their needs and infrastructure
  • Either method ensures full application functionality
  • Sessions using either method have identical features and performance characteristics

This dual-mode approach guarantees that SessionZero remains resilient and accessible regardless of:

  • MSS availability or outages
  • Network configuration complexity
  • User preference for independence vs. convenience
  • Corporate or restrictive network environments

6.4 Data Flow

  1. Client Action: A player (or the GM) updates their character's health in their local client.
  2. Client: The change request is signed with the Player's Private Key and pushed to the Host Authority via the established P2P connection (regardless of connection method used).
  3. Host Authority (Godot): Validates the request (checking signature against stored Public Key), commits the change to its local session data, and broadcasts the updated state to all connected players (including the originator).
  4. Players: Receive the updated session state and apply it to their local model.

6.5 Offline Mode

The application supports full offline functionality, using local storage. When connectivity is restored, the system focuses on session hosting or joining, with conflict-resolution being managed by the Host Authority during session play.

7. Identity and Federation

7.1 Decentralized Player Identity

Users do not have global accounts. Player identity is managed by a cryptographic key pair, granting players full control over their identity and enabling cryptographically verifiable actions.

  • Identity Generation: On first client launch, a permanent Private Key (Secret Token) and corresponding Public Key (Client ID) are generated and stored locally.
  • Private Key (Secret Token): A long, complex string. It is stored securely and never transmitted, only used to sign actions. Players can copy this Private Key to transfer their identity and associated character data to another device.
  • Public Key (Client ID): A shorter, safe-to-share identifier. This is sent to the Host upon connection. The Godot Host stores this Public Key linked to the player's character data to authenticate signatures.
  • Authentication: All communication with the Host is digitally signed by the Player's Private Key and verified by the Host against the stored Public Key.

7.2 SessionZeroDB (Future Decoupled Content Federation)

The large-scale, shared content database concept is decoupled into an entirely separate, future project layer focused on content discovery and distribution, aligning with the federation goals.

  • Content Discovery: This separate project will define APIs and protocols (potentially using RSS-like feeds and WebTorrent/IPFS principles) to index manifests of public Datapacks.
  • Dependency Fulfillment: The core client will use this federated layer to discover, validate, and download Datapacks and dependencies directly from third-party hosting maintainers (self-hosted servers).

8. Licensing

8.1 Source Code License

  • AGPL-3.0 (Affero General Public License): Ensures open-source continuity and attribution, prevents uncredited commercial rebranding.

8.2 User Content License

  • Creative Commons BY-SA 4.0: Allows sharing and adaptation with attribution.

9. Development Phases

Phase 1: Core Platform (MVP)

  • Client (UI, datapack editing, local session management)

  • Networking implementation with the direct connection method:

    • Direct P2P connection using IP/domain with UPnP and manual port-forward support
  • Decentralized Player Identity implementation (key pair generation, signing, and host verification)

  • JSON datapack format implementation and validation tools

Phase 2: Advanced Networking & Content Distribution

  • Minimal Signaling Server (MSS) implementation (Written in Golang)
  • Networking implementation of the MSS-mediated connection method:
    • MSS-mediated connection with UDP Hole Punching and automatic fallbacks (UPnP, direct)
  • Connection stability monitoring and diagnostic tools for both connection methods
  • Initial work on the separate SessionZeroDB API/indexing service for content discovery and download
  • Automatic datapack dependency fulfillment via the SessionZeroDB

Phase 3: Mobile and Plugins

  • Mobile apps with adapted connection workflows
  • Plugin and API extensibility

10. Accessibility & Internationalization

  • Accessibility: Keyboard navigation across all primary views, sufficient color contrast, focus indicators, and screen-reader-friendly labels.
  • Internationalization: Resource-based localization for UI strings; UTF-8 throughout; prepare for RTL support; date/number formatting via invariant + locale-aware overlays.