Factories for templates and objects, Sztl parsing

This commit is contained in:
2025-11-30 21:16:33 -06:00
parent a892d982da
commit 069adb5ff5
10 changed files with 421 additions and 15 deletions

View File

@@ -1,5 +1,8 @@
using System;
using Cogwheel;
using SessionZero.Cogwheel;
using SessionZero.Data;
using SessionZero.Data.Sztl;
namespace SessionZero;
@@ -29,9 +32,97 @@ public static class AppManager
MainWindow.ChangePage(pageName);
}
[Command(Name = "test")]
private static void Test()
public static void TestParseTemplate()
{
COGWHEEL.LogWarning($"Trim size is: {ConsoleControl.OutputTrimSize}");
string toml = @"
[metadata]
template_type = ""data""
id = ""core_item""
uuid = ""0""
data_type = ""item""
version = ""1.0.0""
description = ""Core SessionZero item template""
[name]
type = ""text""
[description]
type = ""textblock""
[consumable]
type = ""bool""
default_value = false
[stats.value]
type = ""number""
[stats.weight]
type = ""number""
[stats.modifiers.base]
type = ""number""
default_value = 1
";
try
{
// Parse template
var template = SztlParser.ParseTemplate(toml);
COGWHEEL.Log($"Template ID: {template.Id}");
COGWHEEL.Log("\nTop-Level Fields:");
foreach (var f in template.Fields)
{
COGWHEEL.Log($"- {f.Key}: Type={f.Value.Type}, Default={f.Value}, TextBlock={f.Value.IsTextBlock}");
}
COGWHEEL.Log("\nGroups:");
foreach (var g in template.SubGroups)
{
PrintGroup(g.Value, "");
}
// --- Instantiate SzDataObject ---
var obj = SzObjectFactory.CreateObject(template);
// Set some values
obj.SetFieldValue("name", "Excalibur");
obj.SetFieldValue("consumable", true);
obj.SetFieldValue("stats.value", 10);
obj.SetFieldValue("stats.weight", 2);
obj.SetFieldValue("stats.modifiers.base", 5);
// Get and log values
COGWHEEL.Log("\nInstanced Object Field Values:");
COGWHEEL.Log($"name = {obj.GetFieldValueAsString("name")}");
COGWHEEL.Log($"consumable = {obj.GetFieldValue<bool>("consumable")}");
COGWHEEL.Log($"stats.value = {obj.GetFieldValue<int>("stats.value")}");
COGWHEEL.Log($"stats.weight = {obj.GetFieldValue<int>("stats.weight")}");
COGWHEEL.Log($"stats.modifiers.base = {obj.GetFieldValue<int>("stats.modifiers.base")}");
}
catch (Exception ex)
{
COGWHEEL.Log($"Error parsing template: {ex.Message}");
}
}
private static void PrintGroup(SztlFieldGroup group, string indent)
{
COGWHEEL.Log($"{indent}Group: {group.Id}");
foreach (var f in group.Fields)
{
COGWHEEL.Log($"{indent} Field: {f.Key}, Type={f.Value.Type}, Default={f.Value}");
}
foreach (var sg in group.SubGroups)
{
PrintGroup(sg.Value, indent + " ");
}
}
}

View File

@@ -6,7 +6,7 @@
x:Class="SessionZero.Cogwheel.ConsoleControl">
<Grid Background="Transparent" RowDefinitions="*, Auto, *" HorizontalAlignment="Stretch">
<ScrollViewer Name="OutputScrollView">
<TextBlock Grid.Row="0" Name="Output" Foreground="White" Background="#7F272727" HorizontalAlignment="Stretch" TextWrapping="Wrap"></TextBlock>
<SelectableTextBlock Grid.Row="0" Name="Output" Foreground="White" Background="#7F272727" HorizontalAlignment="Stretch" TextWrapping="Wrap"></SelectableTextBlock>
</ScrollViewer>
<TextBox Grid.Row="1" Name="Input" Background="#7F272727" Foreground="White" Height="30" AcceptsReturn="False" AcceptsTab="False"></TextBox>
</Grid>

View File

@@ -0,0 +1,6 @@
namespace SessionZero.Data;
public class SzDataObject : SzObject
{
}

View File

@@ -0,0 +1,19 @@
using System;
using Tomlyn.Model;
namespace SessionZero.Data;
public class SzDataObjectTemplate : SzTemplate
{
public string DataType { get; set; }
public override void ParseAdditionalMetaData(TomlTable table)
{
if (!table.ContainsKey("metadata") || table["metadata"] is not TomlTable metadata) return;
if (metadata.TryGetValue("data_type", out var tempTypeVal))
{
DataType = tempTypeVal?.ToString() ?? throw new Exception("Template missing type metadata");
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using SessionZero.Data.Sztl;
@@ -14,21 +15,82 @@ public class SzObject
public virtual object? GetFieldValue(string fieldPath)
{
throw new System.NotImplementedException();
if (string.IsNullOrEmpty(fieldPath)) return null;
var parts = fieldPath.Split('.');
if (parts.Length == 1)
{
if (Fields.TryGetValue(parts[0], out var field))
return field.Value;
return null;
}
SzFieldGroup? currentGroup = null;
for (int i = 0; i < parts.Length - 1; i++)
{
var key = parts[i];
currentGroup = currentGroup == null
? FieldGroups.GetValueOrDefault(key)
: currentGroup.SubGroups.GetValueOrDefault(key);
if (currentGroup == null) return null;
}
var lastPart = parts[^1];
if (currentGroup != null)
{
if (currentGroup.Fields.TryGetValue(lastPart, out var field))
return field.Value;
}
return Fields.TryGetValue(lastPart, out var topField) ? topField.Value : null;
}
public virtual T GetFieldValue<T>(string fieldPath)
{
throw new System.NotImplementedException();
var val = GetFieldValue(fieldPath);
if (val == null) return default!;
return (T)Convert.ChangeType(val, typeof(T));
}
public virtual string GetFieldValueAsString(string fieldPath)
{
throw new System.NotImplementedException();
var val = GetFieldValue(fieldPath);
return val?.ToString() ?? string.Empty;
}
public virtual string SetFieldValue(string fieldPath, object value)
public virtual void SetFieldValue(string fieldPath, object value)
{
throw new System.NotImplementedException();
if (string.IsNullOrEmpty(fieldPath)) return;
var parts = fieldPath.Split('.');
if (parts.Length == 1)
{
if (Fields.ContainsKey(parts[0]))
Fields[parts[0]].Value = value;
return;
}
SzFieldGroup? currentGroup = null;
for (int i = 0; i < parts.Length - 1; i++)
{
var key = parts[i];
currentGroup = currentGroup == null
? FieldGroups.GetValueOrDefault(key)
: currentGroup.SubGroups.GetValueOrDefault(key);
if (currentGroup == null) return;
}
var lastPart = parts[^1];
if (currentGroup != null)
{
if (currentGroup.Fields.ContainsKey(lastPart))
currentGroup.Fields[lastPart].Value = value;
}
else if (Fields.ContainsKey(lastPart))
{
Fields[lastPart].Value = value;
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using SessionZero.Data.Sztl;
namespace SessionZero.Data
{
public static class SzObjectFactory
{
public static SzObject CreateObject(SzTemplate template)
{
ArgumentNullException.ThrowIfNull(template);
SzObject obj = template switch
{
SzDataObjectTemplate _ => new SzDataObject(),
_ => throw new NotImplementedException($"Cannot create object for template type '{template.TemplateType}'")
};
obj.TemplateId = template.Id;
obj.Id = ""; // TODO: This will be replaces with the top level "id" field whenever it gets filled in in the UI
obj.Uuid = "0"; // TODO: Generate true UUIDs later
foreach (var field in template.Fields)
{
obj.Fields[field.Key] = new SzField
{
Id = field.Value.Id,
Type = field.Value.Type,
Value = field.Value.DefaultValue,
IsTextBlock = field.Value.IsTextBlock
};
}
foreach (var group in template.SubGroups)
{
obj.FieldGroups[group.Key] = InitializeGroup(group.Value);
}
return obj;
}
private static SzFieldGroup InitializeGroup(SztlFieldGroup groupTemplate)
{
var group = new SzFieldGroup
{
Id = groupTemplate.Id
};
foreach (var field in groupTemplate.Fields)
{
group.Fields[field.Key] = new SzField
{
Id = field.Value.Id,
Type = field.Value.Type,
Value = field.Value.DefaultValue,
IsTextBlock = field.Value.IsTextBlock
};
}
foreach (var subGroup in groupTemplate.SubGroups)
{
group.SubGroups[subGroup.Key] = InitializeGroup(subGroup.Value);
}
return group;
}
}
}

View File

@@ -1,14 +1,24 @@
using System.Collections.Generic;
using SessionZero.Data.Sztl;
using Tomlyn.Model;
namespace SessionZero.Data;
public class SzTemplate
{
public string TemplateType { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public string DataType { get; set; }
public string Uuid { get; set; }
public string Version { get; set; }
public List<string> CompatibleSystems { get; set; }
public string Description { get; set; }
public Dictionary<string, SztlField> Fields { get; set; } = new();
public Dictionary<string, SztlFieldGroup> SubGroups { get; set; } = new();
public virtual void ParseAdditionalMetaData(TomlTable table)
{
}
}

View File

@@ -2,8 +2,8 @@ namespace SessionZero.Data.Sztl;
public class SztlField
{
public required string Id { get; set; }
public required SzFieldType Type { get; set; }
public string Id { get; set; }
public SzFieldType Type { get; set; }
public object? DefaultValue { get; set; }
public string? Description { get; set; }
public bool IsList { get; set; }

View File

@@ -1,6 +1,152 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Tomlyn;
using Tomlyn.Model;
namespace SessionZero.Data.Sztl;
public static class SztlParser
{
public static SzTemplate ParseTemplate(string tomlText)
{
var doc = Toml.ToModel(tomlText);
if (!doc.ContainsKey("metadata") || doc["metadata"] is not TomlTable metadata)
{
throw new Exception("Template missing the [metadata] section");
}
// Determine template type and fill metadata
string templateType = metadata.TryGetValue("template_type", out var tempTypeVal)
? tempTypeVal?.ToString() ?? throw new Exception("Template missing template_type metadata")
: throw new Exception("Template missing template_type metadata");
SzTemplate template = templateType switch
{
"data" => new SzDataObjectTemplate(),
_ => throw new NotImplementedException($"Unknown template_type '{templateType}'")
};
if (metadata.TryGetValue("id", out var idVal)) template.Id = idVal?.ToString()!;
else throw new Exception("Template missing id metadata");
if (metadata.TryGetValue("uuid", out var uuidVal)) template.Uuid = uuidVal?.ToString()!;
else throw new Exception("Template missing uuid metadata");
if (metadata.TryGetValue("version", out var versionVal)) template.Version = versionVal?.ToString()!;
else throw new Exception("Template missing version metadata");
if (metadata.TryGetValue("compatible_systems", out var compSysVal) && compSysVal is string[] arr)
template.CompatibleSystems = arr.ToList();
if (metadata.TryGetValue("description", out var descVal))
template.Description = descVal?.ToString()!;
else throw new Exception("Template missing description metadata");
template.ParseAdditionalMetaData(doc);
// Process all other tables as fields or groups
ProcessTable(template, doc);
return template;
}
private static void InsertFieldIntoGroup(SzTemplate template, string[] pathParts, TomlTable table)
{
var groupDict = template.SubGroups;
SztlFieldGroup currentGroup = null!;
for (int i = 0; i < pathParts.Length - 1; i++)
{
var part = pathParts[i];
if (!groupDict.TryGetValue(part, out var grp))
{
grp = new SztlFieldGroup { Id = part };
groupDict[part] = grp;
}
currentGroup = grp;
groupDict = grp.SubGroups;
}
var fieldId = pathParts[^1];
var leafTable = table;
if (table.TryGetValue(fieldId, out var possibleLeaf) && possibleLeaf is TomlTable t)
{
leafTable = t;
}
var field = ParseFieldFromTable(fieldId, leafTable);
currentGroup.Fields[fieldId] = field;
}
private static SztlField ParseFieldFromTable(string id, TomlTable table)
{
var field = new SztlField
{
Id = id
};
if (!table.TryGetValue("type", out var tVal))
{
throw new Exception($"Field '{id}' missing type");
}
var typeStr = tVal?.ToString() ?? throw new Exception($"Field '{id}' has null type");
if (!Enum.TryParse<SzFieldType>(typeStr, ignoreCase: true, out var ftype))
{
throw new Exception($"Field '{id}' has unknown type '{typeStr}'");
}
field.Type = ftype;
if (table.TryGetValue("default_value", out var dVal))
{
field.DefaultValue = dVal;
}
if (ftype == SzFieldType.Textblock)
{
field.IsTextBlock = true;
}
return field;
}
private static void ProcessTable(SzTemplate template, TomlTable table, string parentPath = "")
{
foreach (var (key, value) in table)
{
if (key == "metadata") continue;
var path = string.IsNullOrEmpty(parentPath) ? key : $"{parentPath}.{key}";
if (value is TomlTable t)
{
if (t.ContainsKey("type"))
{
var field = ParseFieldFromTable(key, t);
if (string.IsNullOrEmpty(parentPath))
{
template.Fields[key] = field;
}
else
{
InsertFieldIntoGroup(template, path.Split('.'), t);
}
}
else
{
ProcessTable(template, t, path);
}
}
}
}
}

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls;
using Avalonia.Input;
using Cogwheel;
using SessionZero.Cogwheel;
using SessionZero.Data.Sztl;
using SessionZero.Pages;
namespace SessionZero;
@@ -98,4 +99,7 @@ public partial class MainWindow : Window
_libraryButton?.Click += (_, _) => ChangePage("Library");
_settingsButton?.Click += (_, _) => ChangePage("Settings");
}
}