diff --git a/DaggerFramework/Source/Game.cs b/DaggerFramework/Source/Game.cs index 9a94f80..4cbb883 100644 --- a/DaggerFramework/Source/Game.cs +++ b/DaggerFramework/Source/Game.cs @@ -14,6 +14,7 @@ namespace DaggerFramework LoadResources(); Ready(); Run(); + Shutdown(); } /// diff --git a/DaggerFramework/Source/Resources/Loaders/FontLoader.cs b/DaggerFramework/Source/Resources/Loaders/FontLoader.cs index 2e52c4a..ff82573 100644 --- a/DaggerFramework/Source/Resources/Loaders/FontLoader.cs +++ b/DaggerFramework/Source/Resources/Loaders/FontLoader.cs @@ -1,15 +1,13 @@ namespace DaggerFramework.Resources; -public class FontLoader : IResourceLoader +public class FontLoader : IResourceLoader { public IEnumerable SupportedExtensions => new string[] { "ttf" }; - public Type ResourceType => typeof(Font); - - public Resource Load(string path) + public Font Load(string path) { byte[] fileBuffer = File.ReadAllBytes(path); var result = new Font(path, fileBuffer); diff --git a/DaggerFramework/Source/Resources/Loaders/IResourceLoader.cs b/DaggerFramework/Source/Resources/Loaders/IResourceLoader.cs index 79585ca..f6686ed 100644 --- a/DaggerFramework/Source/Resources/Loaders/IResourceLoader.cs +++ b/DaggerFramework/Source/Resources/Loaders/IResourceLoader.cs @@ -1,9 +1,8 @@ namespace DaggerFramework.Resources { - public interface IResourceLoader + public interface IResourceLoader where T : Resource { public IEnumerable SupportedExtensions { get; } - public Type ResourceType { get; } - public Resource Load(string path); + public T Load(string path); } } \ No newline at end of file diff --git a/DaggerFramework/Source/Resources/Loaders/SoundLoader.cs b/DaggerFramework/Source/Resources/Loaders/SoundLoader.cs index 0a934c7..dcfb17b 100644 --- a/DaggerFramework/Source/Resources/Loaders/SoundLoader.cs +++ b/DaggerFramework/Source/Resources/Loaders/SoundLoader.cs @@ -2,16 +2,14 @@ using StbVorbisSharp; namespace DaggerFramework.Resources { - public class SoundLoader : IResourceLoader + public class SoundLoader : IResourceLoader { public IEnumerable SupportedExtensions => new string[] { "ogg" }; - public Type ResourceType => typeof(Sound); - - public Resource Load(string path) + public Sound Load(string path) { Vorbis vorbis; Sound result; diff --git a/DaggerFramework/Source/Resources/Loaders/Texture2dLoader.cs b/DaggerFramework/Source/Resources/Loaders/Texture2dLoader.cs index 7e9e630..01e12a3 100644 --- a/DaggerFramework/Source/Resources/Loaders/Texture2dLoader.cs +++ b/DaggerFramework/Source/Resources/Loaders/Texture2dLoader.cs @@ -3,7 +3,7 @@ using StbImageSharp; namespace DaggerFramework { - public class Texture2dLoader : IResourceLoader + public class Texture2dLoader : IResourceLoader { public IEnumerable SupportedExtensions => new string[] { @@ -12,9 +12,7 @@ namespace DaggerFramework ".jpeg" }; - public Type ResourceType => typeof(Texture2d); - - public Resource Load(string path) + public Texture2d Load(string path) { ImageResult image; using (var stream = File.OpenRead(path)) @@ -28,10 +26,5 @@ namespace DaggerFramework return result; } - - Resource IResourceLoader.Load(string path) - { - throw new NotImplementedException(); - } } } \ No newline at end of file diff --git a/DaggerFramework/Source/Resources/Resource.cs b/DaggerFramework/Source/Resources/Resource.cs index aee1dcf..cdd593c 100644 --- a/DaggerFramework/Source/Resources/Resource.cs +++ b/DaggerFramework/Source/Resources/Resource.cs @@ -1,10 +1,15 @@ +using System.Text.Json.Serialization; + namespace DaggerFramework { public abstract class Resource : IDisposable { public string? Path { get => _path; set => _path = value; } + + [JsonIgnore] public byte[]? Buffer { get => _buffer; set => _buffer = value; } + [JsonIgnore] public long BufferSize { get; set; } public Resource(string path, byte[] buffer) diff --git a/DaggerFramework/Source/Resources/ResourceManager.cs b/DaggerFramework/Source/Resources/ResourceManager.cs index 38efd58..93b8cd0 100644 --- a/DaggerFramework/Source/Resources/ResourceManager.cs +++ b/DaggerFramework/Source/Resources/ResourceManager.cs @@ -24,7 +24,7 @@ namespace DaggerFramework.Resources _logger.Info($"Loading {path} as {typeof(T)} with id \"{resourceId}\"..."); - if (!TryGetLoader(out IResourceLoader? loader)) + if (!TryGetLoader(out IResourceLoader? loader)) { return false; } @@ -50,6 +50,21 @@ namespace DaggerFramework.Resources return true; } + public bool TrySave(string path, in T resource) where T : Resource + { + if (!TryGetSaver(out IResourceSaver? saver)) + { + return false; + } + + if (!saver.TrySave(path, in resource)) + { + return false; + } + + return true; + } + public bool TryGetResource(string resourceId, [NotNullWhen(true)] out T? resource) where T : Resource { resource = null; @@ -75,37 +90,82 @@ namespace DaggerFramework.Resources public bool IsResourceLoaded(string resourceId) => _loadedResources.ContainsKey(resourceId); - public void AddResourceAssociation(Type resourceType, IResourceLoader loader) + public void AddResourceLoaderAssociation(IResourceLoader loader) where T : Resource { - _resourceLoaderAssociations.Add(resourceType, loader); + _logger.Info($"Added resource loader association for {typeof(T)}."); + _resourceLoaderAssociations.Add(typeof(T), loader); } - private bool TryGetLoader([NotNullWhen(true)] out IResourceLoader? loader) where T : Resource + public void AddResourceSaverAssociation(IResourceSaver saver) where T : Resource + { + _logger.Info($"Added resource saver association for {typeof(T)}."); + _resourceSaverAssociations.Add(typeof(T), saver); + } + + private bool TryGetLoader([NotNullWhen(true)] out IResourceLoader? loader) where T : Resource { loader = null; if (!_resourceLoaderAssociations.ContainsKey(typeof(T))) { - _logger.Error($"No loader for {typeof(T).ToString()} was found!"); + _logger.Error($"No loader association found for {typeof(T)}."); return false; } - loader = _resourceLoaderAssociations[typeof(T)]; + loader = _resourceLoaderAssociations[typeof(T)] as IResourceLoader; - _logger.Info($"Using {loader.GetType().ToString()} for loading..."); + if (loader is not null) + { + _logger.Info($"Using {loader.GetType()} for loading..."); + } + else + { + _logger.Error($"No loader association found for {typeof(T)}."); + return false; + } + + return true; + } + + private bool TryGetSaver([NotNullWhen(true)] out IResourceSaver? saver) where T : Resource + { + saver = null; + + if (!_resourceSaverAssociations.ContainsKey(typeof(T))) + { + _logger.Error($"No saver association found for {typeof(T)}."); + return false; + } + + saver = _resourceSaverAssociations[typeof(T)] as IResourceSaver; + + if (saver is not null) + { + _logger.Info($"Using {saver.GetType()} for saving..."); + } + else + { + _logger.Error($"No saver association found for {typeof(T)}."); + return false; + } return true; } private Logger _logger = new(nameof(ResourceManager)); - private readonly Dictionary _resourceLoaderAssociations = new() + private readonly Dictionary _resourceLoaderAssociations = new() { {typeof(Sound), new SoundLoader()}, {typeof(Texture2d), new Texture2dLoader()}, {typeof(Font), new FontLoader()} }; + private readonly Dictionary _resourceSaverAssociations = new() + { + + }; + private Dictionary _loadedResources = new(); } } \ No newline at end of file diff --git a/DaggerFramework/Source/Resources/Savers/IResourceSaver.cs b/DaggerFramework/Source/Resources/Savers/IResourceSaver.cs new file mode 100644 index 0000000..dfd5b27 --- /dev/null +++ b/DaggerFramework/Source/Resources/Savers/IResourceSaver.cs @@ -0,0 +1,7 @@ +namespace DaggerFramework.Resources +{ + public interface IResourceSaver where T : Resource + { + public bool TrySave(string path, in T resource); + } +} \ No newline at end of file diff --git a/DaggerFramework/Source/SceneGraph/Entities/Entity.cs b/DaggerFramework/Source/SceneGraph/Entities/Entity.cs index 18b50dc..d986f18 100644 --- a/DaggerFramework/Source/SceneGraph/Entities/Entity.cs +++ b/DaggerFramework/Source/SceneGraph/Entities/Entity.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using DaggerFramework.Audio; using DaggerFramework.Rendering; @@ -26,4 +27,11 @@ namespace DaggerFramework.SceneGraph Layer?.DestroyEntity(Id); } } + + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(Entity))] + internal partial class EntitySourceGenerationContext : JsonSerializerContext + { + + } } \ No newline at end of file diff --git a/DaggerFramework/Source/SceneGraph/EntityLayer.cs b/DaggerFramework/Source/SceneGraph/EntityLayer.cs index a5f5572..ccd6f07 100644 --- a/DaggerFramework/Source/SceneGraph/EntityLayer.cs +++ b/DaggerFramework/Source/SceneGraph/EntityLayer.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using DaggerFramework.Rendering; using DaggerFramework.Utils; @@ -5,17 +6,17 @@ namespace DaggerFramework.SceneGraph { public class EntityLayer : Layer { - public List Entities { get => _entities; } + [JsonInclude] public List Entities { get; set; } public Camera2d? CurrentCamera { get; set; } public EntityLayer(List entities) { - _entities = entities; + Entities = entities; } public EntityLayer() { - _entities = new List(); + Entities = new List(); } public void UpdateCurrentCamera() @@ -53,14 +54,14 @@ namespace DaggerFramework.SceneGraph public void DestroyEntity(int at) { - _entities.RemoveAt(at); + Entities.RemoveAt(at); } protected override void OnStart() { - for (int i = 0; i < _entities.Count; i++) + for (int i = 0; i < Entities.Count; i++) { - var entity = _entities[i]; + var entity = Entities[i]; entity.Layer = this; entity.Start(); } @@ -68,7 +69,7 @@ namespace DaggerFramework.SceneGraph protected override void OnUpdate(double dt) { - foreach (var entity in _entities) + foreach (var entity in Entities) { entity.Update(dt); } @@ -94,7 +95,7 @@ namespace DaggerFramework.SceneGraph protected override void OnDraw(Renderer renderer) { // TODO: can be done more efficiently, needs rendering redesign. - foreach (var entity in _entities) + foreach (var entity in Entities) { if (entity is IDrawable drawable) { @@ -102,8 +103,6 @@ namespace DaggerFramework.SceneGraph } } } - - private List _entities; private List _cameraEntities = new(); } } \ No newline at end of file diff --git a/DaggerFramework/Source/SceneGraph/Layer.cs b/DaggerFramework/Source/SceneGraph/Layer.cs index 1982762..73d6a72 100644 --- a/DaggerFramework/Source/SceneGraph/Layer.cs +++ b/DaggerFramework/Source/SceneGraph/Layer.cs @@ -1,13 +1,14 @@ using DaggerFramework.Resources; using DaggerFramework.Rendering; +using System.Text.Json.Serialization; namespace DaggerFramework.SceneGraph { public abstract class Layer : IDrawable { - public Scene? Scene { get; set; } - public InputHandler? Input { get; set; } - public ResourceManager ResourceManager => Scene!.ResourceManager; + [JsonIgnore] public Scene? Scene { get; set; } + [JsonIgnore] public InputHandler? Input { get; set; } + [JsonIgnore] public ResourceManager ResourceManager => Scene!.ResourceManager; public void BeginDraw(Renderer renderer) => OnBeginDraw(renderer); public void Draw(Renderer renderer) => OnDraw(renderer); diff --git a/DaggerFramework/Source/SceneGraph/Resources/SerializedScene.cs b/DaggerFramework/Source/SceneGraph/Resources/SerializedScene.cs new file mode 100644 index 0000000..858ef03 --- /dev/null +++ b/DaggerFramework/Source/SceneGraph/Resources/SerializedScene.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace DaggerFramework.SceneGraph +{ + public class SerializedScene : Resource + { + public Dictionary Layers { get; set; } + + public SerializedScene(string path, byte[] buffer) : base(path, buffer) + { + } + } + + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(SerializedScene))] + internal partial class SerializedSceneContext : JsonSerializerContext + { + + } +} \ No newline at end of file diff --git a/DaggerFramework/Source/SceneGraph/Resources/SerializedSceneSaver.cs b/DaggerFramework/Source/SceneGraph/Resources/SerializedSceneSaver.cs new file mode 100644 index 0000000..782c955 --- /dev/null +++ b/DaggerFramework/Source/SceneGraph/Resources/SerializedSceneSaver.cs @@ -0,0 +1,23 @@ +using DaggerFramework.Resources; +using DaggerFramework.Utils; + +namespace DaggerFramework.SceneGraph +{ + public class SerializedSceneSaver : IResourceSaver + { + public bool TrySave(string path, in SerializedScene resource) + { + if (resource.Buffer is null) + { + _logger.Error($"Tried to save a resource at \"{path}\" with a null buffer!"); + return false; + } + + File.WriteAllBytes(path, resource.Buffer); + + return true; + } + + private Logger _logger = new(nameof(SerializedSceneSaver)); + } +} \ No newline at end of file diff --git a/DaggerFramework/Source/SceneGraph/Scene.cs b/DaggerFramework/Source/SceneGraph/Scene.cs index 06b0913..3970d28 100644 --- a/DaggerFramework/Source/SceneGraph/Scene.cs +++ b/DaggerFramework/Source/SceneGraph/Scene.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Text.Json; using DaggerFramework.Audio; using DaggerFramework.Rendering; using DaggerFramework.Resources; @@ -16,20 +16,43 @@ namespace DaggerFramework.SceneGraph public double DeltaTime => _renderer.FrameTime; public bool ShouldRun => Renderer.ShouldRun; - public Scene(Renderer renderer, InputHandler input, AudioBackend audioBackend, ResourceManager resourceManager) + public Scene(SceneSettings settings) { - _renderer = renderer; - _input = input; - _audioBackend = audioBackend; - _resourceManager = resourceManager; - - _layers = new Dictionary(); + _renderer = settings.Renderer; + _input = settings.InputHandler; + _audioBackend = settings.AudioBackend; + _resourceManager = settings.ResourceManager; } - public Scene(Renderer renderer, ResourceManager resourceManager) + public static Scene FromSerialized(SerializedScene serializedScene, SceneSettings settings) { - _renderer = renderer; - _resourceManager = resourceManager; + var scene = new Scene(settings); + scene.WithLayers(serializedScene.Layers); + + return scene; + } + + public bool TrySerialize(out SerializedScene serializedScene) + { + serializedScene = new SerializedScene(string.Empty, new byte[] { }) + { + Layers = _layers + }; + + var sourceGenOptions = new JsonSerializerOptions + { + TypeInfoResolver = SerializedSceneContext.Default + }; + + serializedScene.Buffer = JsonSerializer.SerializeToUtf8Bytes(serializedScene, typeof(SerializedScene), sourceGenOptions); + serializedScene.BufferSize = serializedScene.Buffer.LongLength; + + return true; + } + + public void WithLayers(Dictionary layers) + { + _layers = layers; } public void Init() => SetupRenderer(); @@ -97,4 +120,12 @@ namespace DaggerFramework.SceneGraph private InputHandler? _input; private ResourceManager _resourceManager; } + + public struct SceneSettings + { + public Renderer Renderer { get; set; } + public AudioBackend AudioBackend { get; set; } + public InputHandler InputHandler { get; set; } + public ResourceManager ResourceManager { get; set; } + } } \ No newline at end of file diff --git a/TestGame/TestGame.cs b/TestGame/TestGame.cs index efcc75e..0dc5022 100644 --- a/TestGame/TestGame.cs +++ b/TestGame/TestGame.cs @@ -5,6 +5,8 @@ using DaggerFramework.Rendering; using DaggerFramework.Audio; using DaggerFramework.Resources; using DaggerFramework.SceneGraph; +using System.Text.Json; +using System.Text.Json.Serialization; public class TestGame : Game @@ -17,6 +19,8 @@ public class TestGame : Game _audioBackend = new FmodAudioBackend(); _inputHandler = new RaylibInputHandler(); + _resourceManager.AddResourceSaverAssociation(new SerializedSceneSaver()); + _renderer.CreateAndInitialize(new WindowSettings() { Title = "Test Game", @@ -29,7 +33,13 @@ public class TestGame : Game _audioBackend.Initialize(); - _scene = new Scene(_renderer, _inputHandler, _audioBackend, _resourceManager); + _scene = new Scene(new SceneSettings() + { + Renderer = _renderer, + AudioBackend = _audioBackend, + InputHandler = _inputHandler, + ResourceManager = _resourceManager + }); _uiLayer = new UiLayer(); _worldLayer = new EntityLayer(); @@ -71,6 +81,13 @@ public class TestGame : Game _scene.AddLayer("UI", _uiLayer!); + SerializedScene serializedScene; + + if (_scene!.TrySerialize(out serializedScene)) + { + _resourceManager.TrySave(Path.Combine(_resourceManager.ResourceRoot, "main.scene"), in serializedScene); + } + _scene.Start(); } diff --git a/TestGame/TestPlayer.cs b/TestGame/TestPlayer.cs index 95bf585..bb7dc1e 100644 --- a/TestGame/TestPlayer.cs +++ b/TestGame/TestPlayer.cs @@ -5,6 +5,7 @@ using DaggerFramework.Utils; public class TestPlayer : RectangleShape2d { + public float SprintSpeed { get; set; } = 400f; protected override void OnStart() { base.OnStart(); @@ -26,7 +27,7 @@ public class TestPlayer : RectangleShape2d base.OnUpdate(dt); var sprinting = Input.IsActionDown("sprint"); - _speed = sprinting ? 400f : 200f; + _speed = sprinting ? SprintSpeed : 200f; var velocity = Input.GetInputDirection("left", "right", "up", "down") * _speed; Position += velocity * (float)dt;