Cleaning up

This commit is contained in:
Chris Bell 2025-10-17 14:52:38 -05:00
parent 7f7a9434a7
commit ed1827bf04
39 changed files with 0 additions and 1923 deletions

454
.hidden/.gitignore vendored
View File

@ -1,454 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

View File

@ -1,34 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SessionZero.Shared", "src\SessionZero.Shared\SessionZero.Shared.csproj", "{86CD0D66-D47D-41DA-B857-F8371AC6E6A9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SessionZero.Client", "src\SessionZero.Client\SessionZero.Client.csproj", "{A6455219-BBF2-4A67-B13C-449249083703}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SessionZero.Server", "src\SessionZero.Server\SessionZero.Server.csproj", "{54824F5A-0499-4BC9-AC8E-88E943C66256}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "szpack", "tools\szpack\szpack.csproj", "{70FDB950-CAE6-4C38-A476-3FC10BFCF6E6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{86CD0D66-D47D-41DA-B857-F8371AC6E6A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86CD0D66-D47D-41DA-B857-F8371AC6E6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86CD0D66-D47D-41DA-B857-F8371AC6E6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86CD0D66-D47D-41DA-B857-F8371AC6E6A9}.Release|Any CPU.Build.0 = Release|Any CPU
{A6455219-BBF2-4A67-B13C-449249083703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6455219-BBF2-4A67-B13C-449249083703}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6455219-BBF2-4A67-B13C-449249083703}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6455219-BBF2-4A67-B13C-449249083703}.Release|Any CPU.Build.0 = Release|Any CPU
{54824F5A-0499-4BC9-AC8E-88E943C66256}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54824F5A-0499-4BC9-AC8E-88E943C66256}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54824F5A-0499-4BC9-AC8E-88E943C66256}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54824F5A-0499-4BC9-AC8E-88E943C66256}.Release|Any CPU.Build.0 = Release|Any CPU
{70FDB950-CAE6-4C38-A476-3FC10BFCF6E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70FDB950-CAE6-4C38-A476-3FC10BFCF6E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70FDB950-CAE6-4C38-A476-3FC10BFCF6E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70FDB950-CAE6-4C38-A476-3FC10BFCF6E6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -1,354 +0,0 @@
# **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), offering tools for creating and managing TTRPG data such as characters, datasets, templates, and sessions. The backend supports user accounts, a database for shared content (SessionZeroDB), and prepares for future federation capabilities. Phase 2 introduces federation, while Phase 3 focuses on mobile support and plugin extensibility.
### **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 both local use and self-hostable online instances.
* Create a foundation for future federation and mobile support.
## **2. System Overview**
SessionZero consists of three main layers:
1. **Frontend (Client):** Cross-platform desktop application (.NET Avalonia).
2. **Backend (Server):** ASP.NET backend managing user accounts, SessionZeroDB, and P2P connections.
3. **Datapack System:** A structured archive format for storing and sharing game data.
## **3. Technology Stack**
* **Core:** C# / .NET 9.0 (minimal API)
* **Client:** Avalonia UI (cross-platform desktop)
* **Database:** PostgreSQL (primary data store for shared/online content)
* **Offline/Local:** Local file storage, perhaps SQLite for local instances.
* **Networking:** WebSockets for real-time session updates; SignalR/gRPC considered for P2P/federation.
## **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**
```text
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.
```json
{
"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.**
```json
{
"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.
```json
{
"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.
```json
{
"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.
```json
{
"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 (Stored Separately)**
Instances, such as a specific character or a running campaign's state, are typically stored outside of the core, reusable datapacks but follow a similar structure, referencing a template.
```json
{
"szType": "character-instance",
"templateId": "default-character",
"name": "Aelric Stormhand",
"fields": {
"strength": 14,
"dexterity": 11,
"inventory": ["sword", "potion"]
}
}
```
### **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 Model**
A "Session" is a single game instance, hosted by a GM. It uses a centralized, shared document model (SessionZeroDB/PostgreSQL) with real-time updates pushed via WebSockets.
### **6.2 Data Flow**
1. **GM Action:** GM updates an NPC in their local client.
2. **Client:** Pushes the update to the server via API/WebSockets.
3. **Server:** Validates, commits to DB, and broadcasts the change to all connected players (including the GM).
4. **Players:** Receive the update and apply it to their local model.
### **6.3 Offline Mode**
The application supports full offline functionality, using local storage. When connectivity is restored, the system uses conflict-resolution logic (timestamp-based or operational transformation) to reconcile changes with the SessionZeroDB.
## **7. Federation**
### **7.1 Concept**
Federation allows different SessionZero instances (e.g., self-hosted servers) to share and discover content (Datapacks, public Datasets, Templates). This requires standardized APIs for content retrieval and identity verification.
### **7.2 Identity**
* Users have a unique global identifier (UID).
* Datapacks are identified by their UUID (id).
### **7.3 Data Sharing**
Publicly shared Datapacks are discoverable through a federation layer, allowing users to import content from other trusted 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, session management)
* Backend (accounts, SessionZeroDB, P2P prep)
* JSON datapack format implementation
### **Phase 2: Federation**
* Cross-server communication
* Access controls and search indexing
### **Phase 3: Mobile and Plugins**
* Cross-platform mobile apps
* Plugin and API extensibility
## **10. Testing & Quality**
* **Unit Tests:** Core services (DataPackService, StorageService, NetworkingService) with fixtures for characters/datasets/templates.
* **Schema Validation:** CI job validates sample packs against JSON Schemas (see section 5.8) and rejects PRs with invalid packs.
* **Integration Tests:** Import/export round-trips, dependency resolution, and session snapshot/recovery.
* **Compatibility Matrix:** Validate on Windows and Linux for net9.0; browser target smoke tests where applicable.
## **11. 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.

View File

@ -1,2 +0,0 @@
Samples:
- This folder will include sample datapacks

View File

@ -1,15 +0,0 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SessionZero.Client.App"
xmlns:local="using:SessionZero.Client"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@ -1,47 +0,0 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using System.Linq;
using Avalonia.Markup.Xaml;
using SessionZero.Client.ViewModels;
using SessionZero.Client.Views;
namespace SessionZero.Client;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation();
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
private void DisableAvaloniaDataAnnotationValidation()
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@ -1,21 +0,0 @@
using Avalonia;
using System;
namespace SessionZero.Client;
sealed class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}

View File

@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\"/>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.6"/>
<PackageReference Include="Avalonia.Desktop" Version="11.3.6"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.6"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.6"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.6">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SessionZero.Shared\SessionZero.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,30 +0,0 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using SessionZero.Client.ViewModels;
namespace SessionZero.Client;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return null;
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}

View File

@ -1,6 +0,0 @@
namespace SessionZero.Client.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
public string Greeting { get; } = "Welcome to Avalonia!";
}

View File

@ -1,7 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace SessionZero.Client.ViewModels;
public class ViewModelBase : ObservableObject
{
}

View File

@ -1,20 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:SessionZero.Client.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SessionZero.Client.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="SessionZero.Client">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>

View File

@ -1,11 +0,0 @@
using Avalonia.Controls;
namespace SessionZero.Client.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="SessionZero.Client.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

View File

@ -1,41 +0,0 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

View File

@ -1,23 +0,0 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5227",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7090;http://localhost:5227",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7"/>
</ItemGroup>
</Project>

View File

@ -1,6 +0,0 @@
@SessionZero.Server_HostAddress = http://localhost:5227
GET {{SessionZero.Server_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -1,6 +0,0 @@
namespace SessionZero.Shared.Models;
public class CharacterTemplate : TemplateBase
{
}

View File

@ -1,28 +0,0 @@
namespace SessionZero.Shared.Models;
/// <summary>
/// The model for a datapack (used for the szpack.json file).
/// </summary>
public class Datapack
{
public required Guid Id { get; set; }
public required string Name { get; set; }
public required string Version { get; set; }
public required string Author { get; set; }
public required string License { get; set; }
public string Description { get; set; } = string.Empty;
public required DateTime CreatedAt { get; set; }
public required string SessionZeroVersion { get; set; }
public List<DatapackDependency> Dependencies { get; set; } = new();
}
/// <summary>
/// Represents a dependency of a datapack.
/// </summary>
public class DatapackDependency
{
public required Guid Id { get; set; }
public required string Name { get; set; }
public required string Version { get; set; }
}

View File

@ -1,80 +0,0 @@
using SessionZero.Shared.Schema;
namespace SessionZero.Shared.Models;
public class Dataset : SzObject
{
/// <summary>
/// Arbitrary string used to identify the dataset type (e.g. "items", "quests", "npcs", etc.).
/// </summary>
public required string DatasetType { get; set; }
/// <summary>
/// The entries in the dataset.
/// </summary>
public required Dictionary<string, DatasetEntry> Entries { get; set; } = new();
/// <summary>
/// Retrieves a FieldValue from the dataset using a dot-separated path.
/// Format: [EntryName].[FieldName] or [EntryName].[GroupName].[FieldName]
/// </summary>
/// <param name="fieldName">The dot-separated path to the field.</param>
/// <returns>The FieldValue if found, otherwise null.</returns>
public FieldValue? GetField(string fieldName)
{
var split = fieldName.Split('.');
// Path must contain at least EntryName.FieldName (length 2)
if (split.Length < 2 || split.Length > 3)
{
return null;
}
var entryName = split[0];
if (!Entries.TryGetValue(entryName, out var entry))
{
return null;
}
if (split.Length == 2)
{
// Format: EntryName.FieldName (Top-level field)
var fName = split[1];
if (entry.TopLevelFields is null) return null;
return entry.TopLevelFields.TryGetValue(fName, out var value) ? value : null;
}
else
{
// Format: EntryName.GroupName.FieldName
var groupName = split[1];
var fName = split[2];
if (entry.Groups is null) return null;
var dataGroup = entry.Groups.FirstOrDefault(g => g.Name == groupName);
if (dataGroup is null || dataGroup.Fields is null)
{
return null;
}
return dataGroup.Fields.TryGetValue(fName, out var value) ? value : null;
}
}
/// <summary>
/// Sets a FieldValue in the dataset using a dot-separated path.
/// Format: [EntryName].[FieldName] or [EntryName].[GroupName].[FieldName]
/// </summary>
/// <param name="fieldName">The dot-separated path to the field.</param>
/// <param name="value">The value to set.</param>
/// <returns>True if the value was set, false otherwise</returns>
public static bool SetField(string fieldName, FieldValue value)
{
// TODO: Implementation for SetField
return false;
}
}

View File

@ -1,17 +0,0 @@
using SessionZero.Shared.Schema;
namespace SessionZero.Shared.Models;
public class SessionTemplate : TemplateBase
{
/// <summary>
/// Reference to the required Character Template for character creation in this session.
/// </summary>
public required CharacterTemplateLink CharacterTemplateLink { get; set; }
/// <summary>
/// List of required Datasets that this session template depends on
/// (datapack ids must be present in the DatapackDependency list in the datapack object)
/// </summary>
public required List<DatasetLink> RequiredDatasets { get; set; } = new();
}

View File

@ -1,12 +0,0 @@
namespace SessionZero.Shared.Models;
public class SzObject
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string SzType { get; set; }
public string Description { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public required string Version { get; set; }
public required string SchemaVersion { get; set; }
}

View File

@ -1,11 +0,0 @@
using SessionZero.Shared.Schema;
namespace SessionZero.Shared.Models;
/// <summary>
/// Base class for templates.
/// </summary>
public class TemplateBase : SzObject
{
public required List<TemplateSection> Sections { get; set; } = new();
}

View File

@ -1,120 +0,0 @@
namespace SessionZero.Shared.Schema;
/// <summary>
/// Valid types for fields.
/// </summary>
public enum FieldType
{
Text,
MultiText,
Number,
Boolean,
Formula,
List
}
/// <summary>
/// Represents a list of allowed values for a field with type List.
/// </summary>
public class SzListType
{
// NOTE: Only one of these lists should be allowed
public List<string>? AllowedDatasetTypes { get; set; }
public List<DatasetLink>? AllowedDatasetIds { get; set; }
public string? CustomValues { get; set; }
}
/// <summary>
/// Represents a formula field.
/// </summary>
public class SzFormulaType
{
// TODO: Implement a way to parse formulas
public string? Formula { get; set; }
}
/// <summary>
/// Used to link a dataset in other data or template objects.
/// </summary>
public class DatasetLink
{
public required Guid DatapackId { get; set; }
public required string DatasetId { get; set; }
public required string Version { get; set; }
}
/// <summary>
/// Used to link a character template in other template objects.
/// </summary>
public class CharacterTemplateLink
{
public required Guid DatapackId { get; set; }
public required string TemplateId { get; set; }
public required string Version { get; set; }
}
/// <summary>
/// Represents a field value.
/// </summary>
public class FieldValue
{
public required FieldType Type { get; set; }
public required object Value { get; set; }
}
/// <summary>
/// Represents a group of fields in a dataset.
/// </summary>
public class DataGroup
{
public required string Id { get; set; }
public required string Name { get; set; }
public required Dictionary<string, FieldValue> Fields { get; set; } = new();
}
/// <summary>
/// Represents a dataset entry used in datasets.
/// </summary>
public class DatasetEntry
{
public required string Id { get; set; }
public required string Name { get; set; }
public string Description { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public Dictionary<string, FieldValue>? TopLevelFields { get; set; } = new();
public List<DataGroup>? Groups { get; set; } = new();
}
/// <summary>
/// Represents a section in a template that houses multiple groups.
/// </summary>
public class TemplateSection
{
public required string Id { get; set; }
public required string Name { get; set; }
public required List<TemplateGroup> Groups { get; set; } = new();
}
/// <summary>
/// Represents a group of fields in a template.
/// </summary>
public class TemplateGroup
{
public required string Id { get; set; }
public required string Name { get; set; }
public required List<TemplateFieldDefinition>? Fields { get; set; } = new();
}
/// <summary>
/// Represents a field definition in a template
/// (Does not hold actual field values as templates are meant to be filled in upon instance creation).
/// </summary>
public class TemplateFieldDefinition
{
public required string Id { get; set; }
public required string Name { get; set; }
public required FieldType Type { get; set; }
public object? DefaultValue { get; set; }
public DatasetLink? DatasetLink { get; set; }
}

View File

@ -1,172 +0,0 @@
using System.Text.Json;
using SessionZero.Shared.Models;
using System.IO;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IO.Compression;
namespace SessionZero.Shared.Services;
public static class DatapackService
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
/// <summary>
/// Creates a new Datapack model with required metadata.
/// </summary>
/// <param name="name">The display name of the datapack.</param>
/// <param name="version">The initial version string (e.g., "1.0.0").</param>
/// <param name="author">The pack creator's name.</param>
/// <param name="license">The license (e.g., "CC-BY-SA-4.0").</param>
/// <returns>A fully initialized Datapack model.</returns>
public static Datapack CreateEmptyDatapack(string name, string version, string author, string license)
{
return new Datapack
{
Id = Guid.NewGuid(),
Name = name,
Version = version,
Author = author,
License = license,
Description = String.Empty,
CreatedAt = DateTime.UtcNow,
SessionZeroVersion = "0.0.1",
Dependencies = new()
};
}
/// <summary>
/// Serializes a Datapack model and saves it to a file named 'szpack.json'
/// inside a new subdirectory named after the datapack's slug.
/// </summary>
/// <param name="datapack">The Datapack object to save.</param>
/// <param name="parentDirectoryPath">The directory where the new datapack folder will be created.</param>
/// <returns>The full path to the created szpack.json file.</returns>
public static async Task<string> SaveDatapackMetadataAsync(Datapack datapack, string parentDirectoryPath)
{
var packDirectoryName = datapack.Name.ToLower().Replace(' ', '-').Trim();
var packRootDirectory = Path.Combine(parentDirectoryPath, packDirectoryName);
Directory.CreateDirectory(packRootDirectory);
var filePath = Path.Combine(packRootDirectory, "szpack.json");
var jsonString = JsonSerializer.Serialize(datapack, JsonOptions);
await File.WriteAllTextAsync(filePath, jsonString);
return filePath;
}
/// <summary>
/// Serializes an SzObject (Dataset, Template, etc.) and saves it to a file.
/// </summary>
/// <typeparam name="T">The type of the object, must inherit from SzObject.</typeparam>
/// <param name="szObject">The object to save.</param>
/// <param name="directoryPath">The specific subdirectory (e.g., 'datasets') within the datapack root.</param>
/// <returns>The full path to the created JSON file.</returns>
public static async Task<string> SaveSzObjectAsync<T>(T szObject, string directoryPath) where T : SzObject
{
Directory.CreateDirectory(directoryPath);
var fileName = $"{szObject.Id}.json";
var filePath = Path.Combine(directoryPath, fileName);
var jsonString = JsonSerializer.Serialize(szObject, JsonOptions);
await File.WriteAllTextAsync(filePath, jsonString);
return filePath;
}
/// <summary>
/// Defines and creates the standard directory structure for a SessionZero datapack.
/// </summary>
/// <param name="rootPath">The root directory path of the new datapack.</param>
/// <returns>A dictionary containing the standard folder names and their absolute paths.</returns>
public static Dictionary<string, string> CreateDatapackDirectoryStructure(string rootPath)
{
var structure = new Dictionary<string, string>
{
{ "datasets", Path.Combine(rootPath, "datasets") },
{ "character_templates", Path.Combine(rootPath, "characters") },
{ "session_templates", Path.Combine(rootPath, "sessions") },
{ "media", Path.Combine(rootPath, "media") },
{ "images", Path.Combine(rootPath, "media", "images") }
};
foreach (var path in structure.Values)
{
Directory.CreateDirectory(path);
}
return structure;
}
/// <summary>
/// Compresses a datapack directory into a .szpack zip archive.
/// </summary>
/// <param name="sourceDirectoryPath">The path to the datapack's root directory.</param>
/// <param name="outputFilePath">The full path and filename for the output .szpack file.</param>
public static void PackDatapack(string sourceDirectoryPath, string outputFilePath)
{
if (!Directory.Exists(sourceDirectoryPath))
{
throw new DirectoryNotFoundException($"Source directory not found: {sourceDirectoryPath}");
}
if (File.Exists(outputFilePath))
{
File.Delete(outputFilePath);
}
ZipFile.CreateFromDirectory(sourceDirectoryPath, outputFilePath);
}
/// <summary>
/// Extracts a .szpack zip archive into a target directory.
/// </summary>
/// <param name="sourceFilePath">The path to the .szpack file.</param>
/// <param name="destinationDirectoryPath">The directory where the contents will be extracted.</param>
public static void UnpackDatapack(string sourceFilePath, string destinationDirectoryPath)
{
if (!File.Exists(sourceFilePath))
{
throw new FileNotFoundException($"Datapack file not found: {sourceFilePath}");
}
Directory.CreateDirectory(destinationDirectoryPath);
ZipFile.ExtractToDirectory(sourceFilePath, destinationDirectoryPath, overwriteFiles: true);
}
/// <summary>
/// Deserializes and loads the core Datapack metadata (szpack.json) from a directory.
/// </summary>
/// <param name="datapackRootPath">The path to the datapack's root directory.</param>
/// <returns>A Datapack model containing the metadata.</returns>
/// <exception cref="FileNotFoundException">Thrown if szpack.json is not found.</exception>
/// <exception cref="JsonException">Thrown if deserialization fails.</exception>
public static async Task<Datapack> LoadDatapackMetadataAsync(string datapackRootPath)
{
var filePath = Path.Combine(datapackRootPath, "szpack.json");
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"The required metadata file 'szpack.json' was not found in: {datapackRootPath}", filePath);
}
var jsonString = await File.ReadAllTextAsync(filePath);
var datapack = JsonSerializer.Deserialize<Datapack>(jsonString, JsonOptions);
if (datapack is null)
{
throw new JsonException($"Failed to deserialize szpack.json from {filePath}. The file may be corrupt or invalid.");
}
return datapack;
}
}

View File

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -1,45 +0,0 @@
using SessionZero.Shared.Models;
namespace SessionZero.Shared.Validation;
public static class DatapackValidator
{
public static List<string> ValidateDatapack(Datapack datapack)
{
var errors = new List<string>();
if (datapack.Id == Guid.Empty) errors.Add("Datapack 'Id' is required and must not be an empty GUID.");
ValidateRequiredString(errors, datapack.Name, nameof(datapack.Name));
ValidateRequiredString(errors, datapack.Version, nameof(datapack.Version));
ValidateRequiredString(errors, datapack.Author, nameof(datapack.Author));
ValidateRequiredString(errors, datapack.License, nameof(datapack.License));
ValidateRequiredString(errors, datapack.SessionZeroVersion, nameof(datapack.SessionZeroVersion));
if (datapack.CreatedAt == DateTime.MinValue) errors.Add("Datapack 'CreatedAt' is required and must not be an empty DateTime.");
return errors;
}
public static List<string> ValidateSzObject(SzObject szObject)
{
var errors = new List<string>();
var objectName = szObject.Id;
ValidateRequiredString(errors, szObject.Id, $"{objectName}'s Id");
ValidateRequiredString(errors, szObject.Name, $"{objectName}'s Name");
ValidateRequiredString(errors, szObject.SzType, $"{objectName}'s SzType");
ValidateRequiredString(errors, szObject.Version, $"{objectName}'s Version");
ValidateRequiredString(errors, szObject.SchemaVersion, $"{objectName}'s SchemaVersion");
// 2. Validate Icon path format (optional/future: e.g., only allows alphanumeric + slashes + extension)
// For now, we'll only check if it's not null, which is done by the property definition in SzObject.
return errors;
}
private static void ValidateRequiredString(List<string> errors, string? value, string fieldName)
{
if (string.IsNullOrWhiteSpace(value)) errors.Add($"'{fieldName}' is required and must not be empty.");
}
}

View File

@ -1,107 +0,0 @@
/*
* WARNING:
* This tool was created by an LLM based on the SessionZero shared lib project, therefore it is subject to errors and does not reflect the architecture of the SessionZero project.
* It was created to be used as a quick and dirty validation tool for the szpack format.
*/
using SessionZero.Shared.Models;
using SessionZero.Shared.Services;
using Spectre.Console;
using Spectre.Console.Cli;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace SessionZero.Tools.Packer;
// Settings class defines the command-line arguments
public class CreateSettings : CommandSettings
{
[CommandArgument(0, "<name>")]
[Description("The display name of the datapack (e.g., 'My Fantasy Spells').")]
public required string Name { get; init; }
[CommandArgument(1, "[author]")]
[Description("The author's name.")]
[DefaultValue("Test Author")]
public string Author { get; init; } = "Test Author";
[CommandOption("-o|--output")]
[Description("The parent directory where the new pack folder will be created.")]
[DefaultValue(".")]
public string Output { get; init; } = Environment.CurrentDirectory;
}
public class CreateCommand : AsyncCommand<CreateSettings>
{
public override async Task<int> ExecuteAsync([NotNull] CommandContext context, [NotNull] CreateSettings settings, CancellationToken cancellationToken)
{
AnsiConsole.MarkupLine($"\n[bold white on blue] --- Creating Datapack: '{settings.Name}' --- [/]");
try
{
// 1. Create the top-level model and save szpack.json
var newPack = DatapackService.CreateEmptyDatapack(settings.Name, "1.0.0", settings.Author, "MIT");
var szpackPath = await DatapackService.SaveDatapackMetadataAsync(newPack, settings.Output);
var packRootDirectory = Path.GetDirectoryName(szpackPath)!;
AnsiConsole.MarkupLine($"[green]✅ Created metadata file at: {szpackPath}[/]");
// 2. Create the standard directory structure
var structure = DatapackService.CreateDatapackDirectoryStructure(packRootDirectory);
AnsiConsole.MarkupLine($"[green]✅ Created standard directories at: {packRootDirectory}[/]");
// 3. Create and save test objects
await CreateTestObjects(packRootDirectory, structure);
AnsiConsole.MarkupLine("\n[bold]Creation Complete![/] Use 'szpack pack' to compress it.");
return 0;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"\n[bold white on red]❌ ERROR:[/] Failed to create datapack. Details: {ex.Message}");
return 1;
}
}
// Helper method for creating test objects (copied from previous Program.cs)
private static async Task CreateTestObjects(string rootPath, Dictionary<string, string> structure)
{
// ... (Test object creation logic remains the same, ensure you have the necessary usings in this file)
// --- Test object creation logic (omitted for brevity, assume the previous logic is here) ---
var testDataset = new Dataset { /* ... test data ... */ Id = "basic-attributes", Name = "Basic Character Attributes", SzType = "Dataset", Version = "1.0.0", SchemaVersion = "1.0.0", DatasetType = "Attribute", Entries = new() };
var dsPath = await DatapackService.SaveSzObjectAsync(testDataset, structure["datasets"]);
AnsiConsole.MarkupLine($" -> Saved Test Dataset: [yellow]{Path.GetFileName(dsPath)}[/]");
var testCharTemplate = new CharacterTemplate { /* ... test data ... */ Id = "default-char-sheet", Name = "Default Character Template", SzType = "Template", Version = "1.0.0", SchemaVersion = "1.0.0", Sections = new() };
var charPath = await DatapackService.SaveSzObjectAsync(testCharTemplate, structure["character_templates"]);
AnsiConsole.MarkupLine($" -> Saved Test Character Template: [yellow]{Path.GetFileName(charPath)}[/]");
var testSessionTemplate = new SessionTemplate
{
/* ... test data ... */ Id = "basic-encounter",
Name = "Basic Encounter Template",
SzType = "Template",
Version = "1.0.0",
SchemaVersion = "1.0.0",
CharacterTemplateLink = new()
{
DatapackId = Guid.Empty,
TemplateId = testCharTemplate.Id,
Version = testCharTemplate.Version
},
RequiredDatasets = new()
{
new()
{
DatapackId = Guid.Empty,
DatasetId = testDataset.Id,
Version = testDataset.Version
}
},
Sections = new()
};
var sessionPath = await DatapackService.SaveSzObjectAsync(testSessionTemplate, structure["session_templates"]);
AnsiConsole.MarkupLine($" -> Saved Test Session Template: [yellow]{Path.GetFileName(sessionPath)}[/]");
}
}

View File

@ -1,43 +0,0 @@
/*
* WARNING:
* This tool was created by an LLM based on the SessionZero shared lib project, therefore it is subject to errors and does not reflect the architecture of the SessionZero project.
* It was created to be used as a quick and dirty validation tool for the szpack format.
*/
using SessionZero.Shared.Services;
using Spectre.Console;
using Spectre.Console.Cli;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace SessionZero.Tools.Packer;
public class PackSettings : CommandSettings
{
[CommandArgument(0, "<input>")]
[Description("The root directory of the datapack to pack.")]
public required string Input { get; init; }
}
public class PackCommand : Command<PackSettings>
{
public override int Execute([NotNull] CommandContext context, [NotNull] PackSettings settings, CancellationToken cancellationToken)
{
AnsiConsole.MarkupLine($"\n[bold white on blue] --- Packing Datapack: {settings.Input} --- [/]");
try
{
var packDirectoryName = new DirectoryInfo(settings.Input).Name;
var outputFilePath = Path.Combine(Path.GetDirectoryName(settings.Input) ?? Environment.CurrentDirectory, $"{packDirectoryName}.szpack");
DatapackService.PackDatapack(settings.Input, outputFilePath);
AnsiConsole.MarkupLine($"[green]✅ Successfully created archive: {outputFilePath}[/]");
return 0;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"\n[bold white on red]❌ ERROR:[/] Failed to pack datapack. Details: {ex.Message}");
return 1;
}
}
}

View File

@ -1,36 +0,0 @@
/*
* WARNING:
* This tool was created by an LLM based on the SessionZero shared lib project, therefore it is subject to errors and does not reflect the architecture of the SessionZero project.
* It was created to be used as a quick and dirty validation tool for the szpack format.
*/
using Spectre.Console.Cli;
namespace SessionZero.Tools.Packer;
internal class Program
{
static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.SetApplicationName("szpack");
config.AddCommand<CreateCommand>("create")
.WithDescription("Creates a new datapack directory with test objects.");
config.AddCommand<PackCommand>("pack")
.WithDescription("Compresses a datapack directory into a .szpack file.");
config.AddCommand<UnpackCommand>("unpack")
.WithDescription("Extracts a .szpack file and displays its metadata.");
// Optional: Set a default command if no command is specified
// config.SetDefaultCommand<HelpCommand>();
});
return app.Run(args);
}
}

View File

@ -1,55 +0,0 @@
/*
* WARNING:
* This tool was created by an LLM based on the SessionZero shared lib project, therefore it is subject to errors and does not reflect the architecture of the SessionZero project.
* It was created to be used as a quick and dirty validation tool for the szpack format.
*/
using SessionZero.Shared.Services;
using Spectre.Console;
using Spectre.Console.Cli;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace SessionZero.Tools.Packer;
public class UnpackSettings : CommandSettings
{
[CommandArgument(0, "<input>")]
[Description("The path to the .szpack file.")]
public required string Input { get; init; }
[CommandOption("-o|--output")]
[Description("The directory where the pack will be extracted.")]
[DefaultValue("unpacked")]
public string Output { get; init; } = "unpacked";
}
public class UnpackCommand : AsyncCommand<UnpackSettings>
{
public override async Task<int> ExecuteAsync([NotNull] CommandContext context, [NotNull] UnpackSettings settings, CancellationToken cancellationToken)
{
AnsiConsole.MarkupLine($"\n[bold white on blue] --- Unpacking Datapack: {settings.Input} --- [/]");
try
{
// Unpack the archive
DatapackService.UnpackDatapack(settings.Input, settings.Output);
AnsiConsole.MarkupLine($"[green]✅ Successfully unpacked archive to: {settings.Output}[/]");
// Load and display the core data
var datapack = await DatapackService.LoadDatapackMetadataAsync(settings.Output);
AnsiConsole.MarkupLine("\n[bold]--- Datapack Info (szpack.json) ---[/]");
AnsiConsole.MarkupLine($"[yellow]ID:[/]\t\t{datapack.Id}");
AnsiConsole.MarkupLine($"[yellow]Name:[/]\t{datapack.Name}");
AnsiConsole.MarkupLine($"[yellow]Version:[/]\t{datapack.Version}");
AnsiConsole.MarkupLine($"[yellow]Author:[/]\t{datapack.Author}");
return 0;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"\n[bold white on red]❌ ERROR:[/] Failed to unpack or load datapack. Details: {ex.Message}");
return 1;
}
}
}

View File

@ -1,10 +0,0 @@
# SZPACK CLI Tool
## ***WARNING:***
This tool was created by an LLM based on the SessionZero shared lib project, therefore it is subject to errors and does not reflect the architecture of the SessionZero project.
It was created to be used as a quick and dirty validation tool for the szpack format.
## Usage
* `szpack create <name>`: Creates a sample datapack directory with a given name
* `szpack pack <directory>`: Packs a datapack directory into a szpack file (must be a valid datapack)
* `szpack unpack <name.szpack>`: Unpacks a .szpack file into a datapack directory and displays some metadata

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SessionZero.Shared\SessionZero.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.52.1-preview.0.5" />
<PackageReference Include="Spectre.Console.Cli" Version="0.52.1-preview.0.5" />
</ItemGroup>
</Project>

View File

@ -1,2 +0,0 @@
Planned tools:
- szpack: a cli tool for validating and packing szpack archives