Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
EngineState.Definitions.cs
2using Newtonsoft.Json;
3
4namespace Gondwana;
5
6public sealed partial class EngineState
7{
8 // Keep the historic archive property names, but never deserialize a live archive.
9 private sealed class AssetsFileStateEntry
10 {
14 public string FilePath { get; set; } = string.Empty;
18 public string? Password { get; set; }
22 public bool UseEncryption { get; set; }
23 }
24
25 private abstract class DefinitionStateEntry
26 {
30 [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
31 public string? AssetsFilePath { get; set; }
35 [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
36 public string? AssetEntryName { get; set; }
37
38 internal void Validate(string? loosePath, object? definition, object? legacy = null)
39 {
40 bool hasArchive = AssetsFilePath is not null;
41 bool hasEntry = AssetEntryName is not null;
42 if (hasArchive != hasEntry ||
43 (hasArchive && (string.IsNullOrWhiteSpace(AssetsFilePath) || string.IsNullOrWhiteSpace(AssetEntryName))))
44 throw new InvalidDataException($"Packed definition requires both AssetsFilePath and AssetEntryName: '{AssetsFilePath}' / '{AssetEntryName}'.");
45 int sources = (loosePath is not null ? 1 : 0) + (definition is not null ? 1 : 0) +
46 (hasArchive ? 1 : 0) + (legacy is not null ? 1 : 0);
47 if (sources != 1 || (loosePath is not null && string.IsNullOrWhiteSpace(loosePath)))
48 throw new InvalidDataException("Definition state entry requires exactly one source: inline Definition, loose path, or AssetsFilePath + AssetEntryName.");
49 }
50 }
51
52 private static IEnumerable<DefinitionStateEntry> PackedEntries(EngineStateSnapshot snapshot, EngineStateParts parts)
53 {
54 if (parts.HasFlag(EngineStateParts.Tilesheets) && snapshot.Tilesheets is not null)
55 foreach (var entry in snapshot.Tilesheets.Values)
56 if (entry?.AssetsFilePath is not null) yield return entry;
57 if (parts.HasFlag(EngineStateParts.Cycles) && snapshot.Cycles is not null)
58 foreach (var entry in snapshot.Cycles.Values)
59 if (entry?.AssetsFilePath is not null) yield return entry;
60 if (parts.HasFlag(EngineStateParts.Scenes) && snapshot.Scenes is not null)
61 foreach (var entry in snapshot.Scenes)
62 if (entry?.AssetsFilePath is not null) yield return entry;
63 if (parts.HasFlag(EngineStateParts.Audio) && snapshot.Audio?.AssetsFilePath is not null)
64 yield return snapshot.Audio;
65 if (parts.HasFlag(EngineStateParts.Sprites) && snapshot.Sprites?.AssetsFilePath is not null)
66 yield return snapshot.Sprites;
67 }
68
69 private static void ValidateSelectedSources(EngineStateSnapshot snapshot, EngineStateParts parts)
70 {
71 if (parts.HasFlag(EngineStateParts.Tilesheets) && snapshot.Tilesheets is not null)
72 foreach (var entry in snapshot.Tilesheets.Values)
73 entry?.Validate(entry.GtsPath, entry.Definition);
74 if (parts.HasFlag(EngineStateParts.Cycles) && snapshot.Cycles is not null)
75 foreach (var entry in snapshot.Cycles.Values)
76 entry?.Validate(entry.GaniPath, entry.Definition);
77 if (parts.HasFlag(EngineStateParts.Scenes) && snapshot.Scenes is not null)
78 foreach (var entry in snapshot.Scenes)
79 entry?.Validate(entry.GscnPath, entry.Definition, entry.LegacyScene);
80 if (parts.HasFlag(EngineStateParts.Audio) && snapshot.Audio is { } audio)
81 audio.Validate(audio.GsndPath, audio.Definition);
82 if (parts.HasFlag(EngineStateParts.Sprites) && snapshot.Sprites is { } sprites)
83 sprites.Validate(sprites.GsprPath, sprites.Definition, sprites.LegacySprites);
84 }
85
86 private static bool TryPreserve(DefinitionStateEntry entry, DefinitionProvenance? provenance,
87 object definition, DefinitionPersistence persistence, string? baseDirectory, out string? loosePath)
88 {
89 loosePath = null;
90 if (persistence != DefinitionPersistence.PreserveSource || provenance is null || !provenance.Matches(definition))
91 return false;
92 if (provenance.AssetsFilePath is { } archivePath)
93 {
94 entry.AssetsFilePath = MakeRelativePath(archivePath, baseDirectory);
95 entry.AssetEntryName = provenance.AssetEntryName;
96 return true;
97 }
98 if (provenance.FilePath is { } filePath)
99 {
100 loosePath = MakeRelativePath(filePath, baseDirectory);
101 return true;
102 }
103 return false;
104 }
105
106 private static AssetsFile? FindAssetsFile(string path) => AssetsFile.AllAssetsFiles.FirstOrDefault(
107 file => !string.IsNullOrWhiteSpace(file.SourcePath) && string.Equals(
108 Path.GetFullPath(file.SourcePath), Path.GetFullPath(path),
109 OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
110
111 private static Exception DefinitionError(DefinitionStateEntry entry, string? baseDirectory, Exception error) =>
112 entry.AssetsFilePath is null ? error : new InvalidDataException(
113 $"Cannot load packed definition '{entry.AssetEntryName}' from GAF '{ResolvePath(entry.AssetsFilePath, baseDirectory)}'.", error);
114
115 private static T WithDefinitionContext<T>(DefinitionStateEntry entry, string? baseDirectory, Func<T> materialize)
116 {
117 try { return materialize(); }
118 catch (Exception ex) when (entry.AssetsFilePath is not null && ex is not OutOfMemoryException)
119 {
120 throw DefinitionError(entry, baseDirectory, ex);
121 }
122 }
123
124 private static T LoadPacked<T>(DefinitionStateEntry entry, string? baseDirectory, AssetTypes expectedType, Func<AssetsFile, string, T> load)
125 {
126 var path = ResolvePath(entry.AssetsFilePath!, baseDirectory);
127 try
128 {
129 var archive = FindAssetsFile(path);
130 if (archive is null)
131 {
132 if (!File.Exists(path)) throw new FileNotFoundException("Archive not found.", path);
133 archive = AssetsFile.LoadOrCreate(path);
134 }
135 // EngineState references are exact identities, not the convenience basename
136 // aliases accepted by AssetsFile.Get for interactive runtime lookup.
137 if (!archive.GetAllEntries().Any(candidate => candidate.AssetType == expectedType &&
138 string.Equals(candidate.AssetName, entry.AssetEntryName, StringComparison.Ordinal)))
139 throw new InvalidDataException($"Entry does not exist with expected type '{expectedType}'.");
140 return load(archive, entry.AssetEntryName!);
141 }
142 catch (Exception ex) when (ex is not OutOfMemoryException)
143 {
144 throw new InvalidDataException($"Cannot load packed definition '{entry.AssetEntryName}' from GAF '{path}'.", ex);
145 }
146 }
147}
Represents the complete serializable state of the game engine, including assets, scenes,...
AssetTypes
Defines the types of assets that can be stored and managed by the engine.
Definition AssetTypes.cs:8
EngineStateParts
Represents different parts of the engine state that can be managed, saved, or loaded independently....
DefinitionPersistence
Controls how EngineState persists a definition.