diff --git a/Core/Entities/Properties/Meshes/MeshService.cs b/Core/Entities/Properties/Meshes/MeshService.cs index 4245269..67e5300 100644 --- a/Core/Entities/Properties/Meshes/MeshService.cs +++ b/Core/Entities/Properties/Meshes/MeshService.cs @@ -30,14 +30,7 @@ public Mesh LoadMesh(string identifier, Mesh mesh) var vertexBufferObject = Window.GL.GenBuffer(); Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); - - unsafe - { - fixed (float* meshDataPtr = meshData) - { - Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)meshData.Length * sizeof(float), meshDataPtr, GLEnum.StaticDraw); - } - } + Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)meshData.Length * sizeof(float), meshData, GLEnum.StaticDraw); Meshes.Add(identifier, mesh); return mesh; diff --git a/Core/Entities/UIElement.cs b/Core/Entities/UIElement.cs index b0d10a0..30fa3a0 100644 --- a/Core/Entities/UIElement.cs +++ b/Core/Entities/UIElement.cs @@ -63,21 +63,15 @@ public void Initialize() InitializeBuffers(); } - private unsafe void InitializeBuffers() + private void InitializeBuffers() { - fixed (float* vertexDataPtr = _vertices) - { - var vertexBufferObject = Window.GL.GenBuffer(); - Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); - Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)_vertices.Length * sizeof(float), vertexDataPtr, GLEnum.StaticDraw); - } - - fixed (uint* indicieDataPtr = _indices) - { - var elementBufferObject = Window.GL.GenBuffer(); - Window.GL.BindBuffer(GLEnum.ElementArrayBuffer, elementBufferObject); - Window.GL.BufferData(GLEnum.ElementArrayBuffer, (uint)_indices.Length * sizeof(uint), indicieDataPtr, GLEnum.StaticDraw); - } + var vertexBufferObject = Window.GL.GenBuffer(); + Window.GL.BindBuffer(GLEnum.ArrayBuffer, vertexBufferObject); + Window.GL.BufferData(GLEnum.ArrayBuffer, (uint)_vertices.Length * sizeof(float), _vertices, GLEnum.StaticDraw); + + var elementBufferObject = Window.GL.GenBuffer(); + Window.GL.BindBuffer(GLEnum.ElementArrayBuffer, elementBufferObject); + Window.GL.BufferData(GLEnum.ElementArrayBuffer, (uint)_indices.Length * sizeof(uint), _indices, GLEnum.StaticDraw); } /// diff --git a/Core/Interfaces/IGame.cs b/Core/Interfaces/IGame.cs index ed8428f..de0b798 100644 --- a/Core/Interfaces/IGame.cs +++ b/Core/Interfaces/IGame.cs @@ -1,4 +1,5 @@ using SharpEngine.Core.Entities.Views; +using SharpEngine.Core.Entities.Views.Settings; using SharpEngine.Core.Enums; using Silk.NET.Input; @@ -9,50 +10,50 @@ namespace SharpEngine.Core.Interfaces; /// Contains definitions the Game class must implement. /// To consider: move into a abstract class so that the game doesn't necessarily have to implement all methods. /// -public interface IGame +public class Game { /// /// Gets or sets the core settings of the game. /// - public ISettings CoreSettings { get; } + public ISettings CoreSettings { get; init; } = new DefaultSettings(); /// /// Gets or sets the camera of the game. /// - public CameraView Camera { get; set; } + public CameraView Camera { get; set; } = new(new System.Numerics.Vector3(0), new DefaultViewSettings()); /// /// Executed when a mouse button is pressed. /// - public void HandleMouseDown(IMouse mouse, MouseButton button); + public virtual void HandleMouseDown(IMouse mouse, MouseButton button) { } /// /// A method executed when the mouse state is changed /// /// The state of the mouse on the current frame. - public void HandleMouse(IMouse mouse); + public virtual void HandleMouse(IMouse mouse) { } /// /// Gets or sets the event executed when the mouse wheel is scrolled. /// /// The direction the wheel was scrolled. /// Contains information about the scroll wheel changes during the current frame. - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel); + public virtual void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { } /// /// Executed when a key is pressed. /// /// The state of the keyboard on the current frame. /// The time since the last frame. - public void HandleKeyboard(IKeyboard input, double deltaTime); + public virtual void HandleKeyboard(IKeyboard input, double deltaTime) { } /// /// Executed when the game is first loaded. /// - public void Initialize(); + public virtual void Initialize() { } /// /// Executed each frame. /// - public void Update(double deltaTime, IInputContext input); + public virtual void Update(double deltaTime, IInputContext input) { } } diff --git a/Core/Interfaces/ISettings.cs b/Core/Interfaces/ISettings.cs index bf793b3..8ccad16 100644 --- a/Core/Interfaces/ISettings.cs +++ b/Core/Interfaces/ISettings.cs @@ -1,4 +1,5 @@ using SharpEngine.Core.Renderers; +using Silk.NET.Windowing; namespace SharpEngine.Core.Interfaces; @@ -19,4 +20,23 @@ public interface ISettings /// Gets or sets which renderer should be enabled for the current window. public RenderFlags RendererFlags { get; set; } + + /// Gets or sets the settings for a window. + WindowOptions WindowOptions { get; set; } +} + +/// +public class DefaultSettings : ISettings +{ + /// + public bool UseWireFrame { get; set; } + + /// + public bool PrintFrameRate { get; set; } + + /// + public RenderFlags RendererFlags { get; set; } = RenderFlags.All; + + /// + public WindowOptions WindowOptions { get; set; } } diff --git a/Core/Renderers/Renderer.cs b/Core/Renderers/Renderer.cs index 14eed64..807363b 100644 --- a/Core/Renderers/Renderer.cs +++ b/Core/Renderers/Renderer.cs @@ -21,7 +21,7 @@ public class Renderer : RendererBase private LampShader _lampShader; private LightingShader _lightingShader; - private readonly IGame _game; + private readonly Game _game; private readonly Scene _scene; // Read only once, load into OpenGL buffer once. @@ -35,7 +35,7 @@ public class Renderer : RendererBase /// /// The game the renderer is being used for. /// The game scene to be rendered. - public Renderer(IGame game, Scene scene) : base(game.CoreSettings) + public Renderer(Game game, Scene scene) : base(game.CoreSettings) { _game = game; _scene = scene; diff --git a/Core/Renderers/UIRenderer.cs b/Core/Renderers/UIRenderer.cs index 546496d..b7967aa 100644 --- a/Core/Renderers/UIRenderer.cs +++ b/Core/Renderers/UIRenderer.cs @@ -23,7 +23,7 @@ public class UIRenderer : RendererBase /// /// Initializes a new instance of . /// - public UIRenderer(IGame game, Scene scene) : base(game.CoreSettings) + public UIRenderer(Game game, Scene scene) : base(game.CoreSettings) { _scene = scene; diff --git a/Core/SharpEngine.Core.NET.csproj b/Core/SharpEngine.Core.csproj similarity index 89% rename from Core/SharpEngine.Core.NET.csproj rename to Core/SharpEngine.Core.csproj index 3473f2f..44c552b 100644 --- a/Core/SharpEngine.Core.NET.csproj +++ b/Core/SharpEngine.Core.csproj @@ -7,12 +7,15 @@ Required by some low level OpenGL functions. We should see if there is a better way to approach where ever these unsafe contexts are being used. --> - true AnyCPU;x64;x86 $(GenerateNuGetPackages) https://github.com/Av3boy/SharpEngine game-engine; True + + + $(AssemblyName).NET + Debug;Release;LauncherNonWindows diff --git a/Core/SilkWindow.cs b/Core/SilkWindow.cs index 324fa85..b88605b 100644 --- a/Core/SilkWindow.cs +++ b/Core/SilkWindow.cs @@ -13,7 +13,7 @@ namespace SharpEngine.Core; /// /// Represents a class abstraction for the interface. /// -public abstract class SilkWindow : IWindow +public abstract class SilkWindow : IDisposable { /// public virtual IWindowHost? Parent { get; } @@ -39,12 +39,12 @@ public abstract class SilkWindow : IWindow /// public Vector2D Size { get; set; } - /// - public string Title - { - get => CurrentWindow.Title; - set => CurrentWindow.Title = value; - } + // /// + // public string Title + // { + // get => CurrentWindow.Title; + // set => CurrentWindow.Title = value; + // } /// public WindowState WindowState { get; set; } @@ -158,21 +158,21 @@ public virtual void Close() { } /// public virtual void ContinueEvents() { } - private IWindow? _currentWindow; - - /// Gets or sets the current window. - public IWindow CurrentWindow - { - get => _currentWindow ?? this; - set => _currentWindow = value; - } - - /// - public virtual IWindow CreateWindow(WindowOptions opts) - { - CurrentWindow = Silk.NET.Windowing.Window.Create(opts); - return CurrentWindow; - } + // private IWindow? _currentWindow; + // + // /// Gets or sets the current window. + // public IWindow CurrentWindow + // { + // get => _currentWindow!; + // set => _currentWindow = value; + // } + + /// + // 7public IWindow CreateWindow(WindowOptions opts) + // 7{ + // 7 CurrentWindow = Silk.NET.Windowing.Window.Create(opts); + // 7 return CurrentWindow; + // 7} /// protected virtual void Dispose(bool disposing) { } diff --git a/Core/Window.cs b/Core/Window.cs index ecae665..38c7690 100644 --- a/Core/Window.cs +++ b/Core/Window.cs @@ -28,12 +28,15 @@ namespace SharpEngine.Core; /// public class Window : SilkWindow { - private readonly IGame _game; + private readonly Game _game; + private static IWindow _window; private IEnumerable _renderers = []; private ImGuiController? _imGuiController; + public IInputContext Input { get; protected set; } + /// /// The scene that is currently being rendered. /// @@ -46,7 +49,7 @@ public class Window : SilkWindow /// /// Gets the current OpenGL context. /// - /// The OpennGL context for this window. + /// The OpenGL context for this window. public static GL GetGL() => GL; private static void SetGL(GL gl) => GL = gl; @@ -57,22 +60,35 @@ public class Window : SilkWindow /// /// Contains the actual game implementation. /// Contains the game scene. - public Window(IGame game, Scene scene, WindowOptions options) + public Window(Game game, Scene scene, WindowOptions options) { // TODO: Game should be refactored out of the window class. _game = game; Scene = scene; - CreateWindow(options); - _game.Camera = new CameraView(Vector3.UnitZ * 3, new DefaultViewSettings()); + Initialize(options); + } + + public Window(Scene scene, WindowOptions options) + { + _game = new Game(); + Scene = scene; + + Initialize(options); + } + + private void Initialize(WindowOptions options) + { + _window = Silk.NET.Windowing.Window.Create(options); + // _game.Camera = new CameraView(Vector3.UnitZ * 3, new DefaultViewSettings()); - CurrentWindow.Update += OnUpdateFrame; - CurrentWindow.Render += RenderFrame; - CurrentWindow.Resize += OnResize; - CurrentWindow.Load += OnLoad; - CurrentWindow.Closing += OnClosing; + _window.Update += OnUpdateFrame; + _window.Render += RenderFrame; + _window.Resize += OnResize; + _window.Load += OnLoad; + _window.Closing += OnClosing; - CurrentWindow.Run(); + _window.Run(); } private void OnClosing() @@ -85,10 +101,11 @@ public override void OnLoad() { try { - SetGL(CurrentWindow.CreateOpenGL()); + var context = _window.CreateOpenGL(); + SetGL(context); - Input = CurrentWindow.CreateInput(); - CurrentWindow.MakeCurrent(); + Input = _window.CreateInput(); + _window.MakeCurrent(); AssignInputEvents(); @@ -120,16 +137,19 @@ public override void OnLoad() foreach (var renderer in _renderers) renderer.Initialize(); - _imGuiController = new ImGuiController(GL, CurrentWindow, Input); + _imGuiController = new ImGuiController(GL, _window, Input); _initialized = true; } catch (Exception ex) { - Debug.LogInformation(ex.Message); + Debug.LogInformation("Error loading window: " + ex.Message, ex); } } + protected virtual void PreRender(double deltaTime) { } + + /// /// Renders the current view and all specified renderers. /// @@ -172,6 +192,9 @@ protected void RenderFrame(double deltaTime) } } + protected virtual void AfterRender(double deltaTime) { } + + /// /// Toggles the renderer between wireframe and fill mode. /// @@ -213,7 +236,7 @@ protected void OnUpdateFrame(double deltaTime) _game.HandleMouse(mouse); if (keyboard.IsKeyPressed(Key.Escape)) - CurrentWindow.Close(); + _window.Close(); _game.HandleKeyboard(keyboard, deltaTime); @@ -234,11 +257,14 @@ private void AssignInputEvents() } } + public virtual void OnMouseClick(IMouse mouse, MouseButton button, Vector2 vector) { } + + /// protected void KeyDown(IKeyboard keyboard, Key key, int keyCode) { if (key == Key.Escape) - CurrentWindow.Close(); + _window.Close(); // _game.HandleKeyboard(keyboard); } @@ -266,7 +292,7 @@ protected void OnResize(Vector2D size) } /// - protected override void OnMouseDown(IMouse mouse, MouseButton button) + protected void OnMouseDown(IMouse mouse, MouseButton button) => _game.HandleMouseDown(mouse, button); /// @@ -280,8 +306,8 @@ protected void SetScene(Scene scene) } /// - protected override void Dispose(bool disposing) + protected void Dispose(bool disposing) { - base.Dispose(disposing); + //base.Dispose(disposing); } } diff --git a/Editor/Editor.cs b/Editor/Editor.cs index d59a403..5960dfc 100644 --- a/Editor/Editor.cs +++ b/Editor/Editor.cs @@ -6,44 +6,7 @@ namespace SharpEngine.Editor; -public class Editor : IGame +public class Editor : Game { - public ISettings CoreSettings { get; } = new EditorSettings(); - - public CameraView Camera { get; set; } - - public void HandleKeyboard(IKeyboard input, double deltaTime) - { - - } - public void HandleMouse(IMouse mouse) - { - - } - public void HandleMouseDown(IMouse mouse, MouseButton button) - { - - } - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) - { - - } - public void Initialize() - { - - } - - public void Update(double deltaTime, IInputContext input) - { - - } - - public void SaveScene(Scene scene) - { - - } - - public void LoadScene(string sceneFile) - { - } + } \ No newline at end of file diff --git a/Editor/EditorWindow.cs b/Editor/EditorWindow.cs index 576bde5..d1bab81 100644 --- a/Editor/EditorWindow.cs +++ b/Editor/EditorWindow.cs @@ -21,10 +21,9 @@ public class EditorWindow : Window /// /// Initializes a new instance of . /// - /// /// The scene to be initialized. /// The settings for the editor window. - public EditorWindow(IGame game, Scene scene, IViewSettings settings) : base(game, scene, settings.WindowOptions) + public EditorWindow(Scene scene, IViewSettings settings) : base(scene, settings.WindowOptions) { } diff --git a/Editor/Program.cs b/Editor/Program.cs index e01e009..437bb8b 100644 --- a/Editor/Program.cs +++ b/Editor/Program.cs @@ -15,9 +15,9 @@ public static class Program [STAThread] public static void Main(string[] args) { - var editor = new Editor(); + //var editor = new Editor(); var scene = args.Length > 0 ? Scene.LoadScene(args[0]) : new Scene(); - using var window = new EditorWindow(editor, scene, new DefaultViewSettings()); + using var window = new EditorWindow(scene, new DefaultViewSettings()); } } \ No newline at end of file diff --git a/Editor/SharpEngine.Editor.csproj b/Editor/SharpEngine.Editor.csproj index f735be1..9af225b 100644 --- a/Editor/SharpEngine.Editor.csproj +++ b/Editor/SharpEngine.Editor.csproj @@ -15,10 +15,11 @@ Early release, expect broken / missing features. en true + Debug;Release;LauncherNonWindows - + diff --git a/Examples/Block/BlockBase.cs b/Examples/MinecraftClone/Block/BlockBase.cs similarity index 100% rename from Examples/Block/BlockBase.cs rename to Examples/MinecraftClone/Block/BlockBase.cs diff --git a/Examples/Block/Dirt.cs b/Examples/MinecraftClone/Block/Dirt.cs similarity index 100% rename from Examples/Block/Dirt.cs rename to Examples/MinecraftClone/Block/Dirt.cs diff --git a/Examples/Block/Stone.cs b/Examples/MinecraftClone/Block/Stone.cs similarity index 100% rename from Examples/Block/Stone.cs rename to Examples/MinecraftClone/Block/Stone.cs diff --git a/Examples/BlockFactory.cs b/Examples/MinecraftClone/BlockFactory.cs similarity index 100% rename from Examples/BlockFactory.cs rename to Examples/MinecraftClone/BlockFactory.cs diff --git a/Examples/Input.cs b/Examples/MinecraftClone/Input.cs similarity index 100% rename from Examples/Input.cs rename to Examples/MinecraftClone/Input.cs diff --git a/Examples/Inventory.cs b/Examples/MinecraftClone/Inventory.cs similarity index 100% rename from Examples/Inventory.cs rename to Examples/MinecraftClone/Inventory.cs diff --git a/Examples/Game.cs b/Examples/MinecraftClone/Minecraft.cs similarity index 82% rename from Examples/Game.cs rename to Examples/MinecraftClone/Minecraft.cs index 80f3792..a66ee25 100644 --- a/Examples/Game.cs +++ b/Examples/MinecraftClone/Minecraft.cs @@ -4,7 +4,6 @@ using SharpEngine.Core.Scenes; using SharpEngine.Core.Entities; using SharpEngine.Core.Entities.Lights; -using SharpEngine.Core.Entities.Views; using Minecraft.Block; using Silk.NET.Input; @@ -14,47 +13,48 @@ namespace Minecraft; -public class Game : IGame -{ - // TODO: Add documentation to the code - - /// Gets or sets the player camera. - public CameraView Camera { get; set; } - - /// - public ISettings CoreSettings { get; } +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Justification = Those values are set in the Initialize method. +/// +/// Contains the logic for the Minecraft game. +/// +public class Minecraft : Game +{ private readonly Scene _scene; private SceneNode _lightsNode; private SceneNode _blocksNode; private Input _input; - private Inventory _inventory; + private readonly Inventory _inventory; - UIElement uiElem; + private readonly UIElement uiElem; /// - /// Initializes a new instance of the . + /// Initializes a new instance of the . /// - public Game(Scene scene, ISettings settings) + public Minecraft(Scene scene, ISettings settings) { _scene = scene; CoreSettings = settings; + + _inventory = new Inventory(); + // uiElem = new UIElement("uiElement"); } /// - public void Initialize() + public override void Initialize() { + base.Initialize(); + _input = new Input(Camera); - _inventory = new Inventory(); _inventory.Initialize(); _lightsNode = _scene.Root.AddChild("lights"); _blocksNode = _scene.Root.AddChild("blocks"); - uiElem = new UIElement("uiElement"); - _scene.UIElements.Add(uiElem); + // TODO: Fix UI renderer + // _scene.UIElements.Add(uiElem); InitializeWorld(); } @@ -93,7 +93,7 @@ private void InitializeChunks() { // TODO: Generate chunks when player moves - // TODO: Generate chunks using 3d perlin noise + // TODO: Generate chunks using 3d Perlin noise const int chunkSize = 16; const int numChunks = 3; @@ -127,16 +127,16 @@ private void GenerateChunk(int chunkSize, Vector3 chunkPos) } /// - public void Update(double deltaTime, IInputContext input) + public override void Update(double deltaTime, IInputContext input) { - uiElem.Transform.Rotation += 0.01f; + // uiElem.Transform.Rotation += 0.01f; _input.HandleKeyboard(input.Keyboards[0], (float)deltaTime); } // TODO: Input system to let users change change key bindings? /// - public void HandleKeyboard(IKeyboard input, double deltaTime) + public override void HandleKeyboard(IKeyboard input, double deltaTime) { for (int i = 0; i <= 9; i++) { @@ -164,10 +164,10 @@ private void Input_KeyUp(IKeyboard arg1, Key arg2, int arg3) } /// - public void HandleMouse(IMouse mouse) { } + public override void HandleMouse(IMouse mouse) { } /// - public void HandleMouseDown(IMouse mouse, MouseButton button) + public override void HandleMouseDown(IMouse mouse, MouseButton button) { if (button == MouseButton.Right) { @@ -187,13 +187,13 @@ public void HandleMouseDown(IMouse mouse, MouseButton button) if (button == MouseButton.Left) { - var destoryedBlockType = DestroyBlock(); - if (destoryedBlockType != BlockType.None) + var destroyedBlockType = DestroyBlock(); + if (destroyedBlockType != BlockType.None) { // TODO: The block should be added to the slot so that 0 is the last slot instead of 9. - // TODO: The first block destoryed doesn't seem to be added to the inventory. - Console.WriteLine($"Block destroyed: {destoryedBlockType}."); - _inventory.AddToolbarItem(destoryedBlockType); + // TODO: The first block destroyed doesn't seem to be added to the inventory. + Console.WriteLine($"Block destroyed: {destroyedBlockType}."); + _inventory.AddToolbarItem(destroyedBlockType); } } } @@ -244,7 +244,7 @@ public bool IsBlockInView(out GameObject? intersectingObject, out Vector3 hitPos } /// - public void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) + public override void HandleMouseWheel(MouseWheelScrollDirection direction, ScrollWheel scrollWheel) { int slotIndex = _inventory.SelectedSlotIndex; diff --git a/Examples/Minecraft.csproj b/Examples/MinecraftClone/Minecraft.csproj similarity index 77% rename from Examples/Minecraft.csproj rename to Examples/MinecraftClone/Minecraft.csproj index 97ff3b5..89889c9 100644 --- a/Examples/Minecraft.csproj +++ b/Examples/MinecraftClone/Minecraft.csproj @@ -3,10 +3,11 @@ Exe AnyCPU;x64;x86 + Debug;Release;LauncherNonWindows - + diff --git a/Examples/Minecraft.sln b/Examples/MinecraftClone/Minecraft.sln similarity index 100% rename from Examples/Minecraft.sln rename to Examples/MinecraftClone/Minecraft.sln diff --git a/Examples/Program.cs b/Examples/MinecraftClone/Program.cs similarity index 77% rename from Examples/Program.cs rename to Examples/MinecraftClone/Program.cs index 2ec0a7f..59e95fd 100644 --- a/Examples/Program.cs +++ b/Examples/MinecraftClone/Program.cs @@ -1,4 +1,5 @@ -using SharpEngine.Core.Scenes; +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Scenes; using Silk.NET.Maths; using Silk.NET.Windowing; @@ -12,7 +13,7 @@ public static class Program { private static void Main() { - Settings gameSettings = new() + DefaultSettings gameSettings = new() { UseWireFrame = false, WindowOptions = WindowOptions.Default with @@ -23,9 +24,8 @@ private static void Main() }; Scene scene = new Scene(); - Game game = new Game(scene, gameSettings); + Minecraft game = new Minecraft(scene, gameSettings); using var window = new SharpEngine.Core.Window(game, scene, gameSettings.WindowOptions); - window.Run(); } } diff --git a/Examples/Resources/container2.png b/Examples/MinecraftClone/Resources/container2.png similarity index 100% rename from Examples/Resources/container2.png rename to Examples/MinecraftClone/Resources/container2.png diff --git a/Examples/Resources/container2_specular.png b/Examples/MinecraftClone/Resources/container2_specular.png similarity index 100% rename from Examples/Resources/container2_specular.png rename to Examples/MinecraftClone/Resources/container2_specular.png diff --git a/Examples/Resources/grass.jpg b/Examples/MinecraftClone/Resources/grass.jpg similarity index 100% rename from Examples/Resources/grass.jpg rename to Examples/MinecraftClone/Resources/grass.jpg diff --git a/Examples/Minimal/Minimal.csproj b/Examples/Minimal/Minimal.csproj new file mode 100644 index 0000000..2821290 --- /dev/null +++ b/Examples/Minimal/Minimal.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + Debug;Release;LauncherNonWindows + + + + + + + diff --git a/Examples/Minimal/Minimal.sln b/Examples/Minimal/Minimal.sln new file mode 100644 index 0000000..9653d95 --- /dev/null +++ b/Examples/Minimal/Minimal.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35821.62 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minimal", "Minimal\Minimal.csproj", "{D5FCD32F-4279-48B4-ADD9-385A5CB1D145}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Core.NET", "..\..\Core\SharpEngine.Core.NET.csproj", "{66489F2C-3EFC-99B8-140D-3076B120A01E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5FCD32F-4279-48B4-ADD9-385A5CB1D145}.Release|Any CPU.Build.0 = Release|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66489F2C-3EFC-99B8-140D-3076B120A01E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {26D1831D-3120-4E90-BF49-0844EB460F55} + EndGlobalSection +EndGlobal diff --git a/Examples/Minimal/Program.cs b/Examples/Minimal/Program.cs new file mode 100644 index 0000000..d00aca3 --- /dev/null +++ b/Examples/Minimal/Program.cs @@ -0,0 +1,47 @@ +using SharpEngine.Core.Interfaces; +using SharpEngine.Core.Scenes; + +using Silk.NET.Maths; +using Silk.NET.Windowing; + +namespace Minimal; + +/// +/// Represents the entry point of the application. +/// +public static class Program +{ + /// + /// The entry point of the application. + /// + public static void Main(string[] _) + { + var game = new Minimal(new DefaultSettings() + { + UseWireFrame = false, + WindowOptions = WindowOptions.Default with + { + Size = new Vector2D(800, 600), + Title = "Minimal", + } + }); + + var scene = new Scene(); + using var window = new SharpEngine.Core.Window(game, scene, game.CoreSettings.WindowOptions); + } +} + +/// +/// Represents the game instance. +/// +public class Minimal : Game +{ + /// + /// Initializes a new instance of + /// + /// Provides configuration options for the instance. + public Minimal(ISettings settings) + { + CoreSettings = settings; + } +} diff --git a/Examples/Settings.cs b/Examples/Settings.cs deleted file mode 100644 index b5ea3f1..0000000 --- a/Examples/Settings.cs +++ /dev/null @@ -1,22 +0,0 @@ -using SharpEngine.Core.Interfaces; -using SharpEngine.Core.Renderers; - -using Silk.NET.Windowing; - -namespace Minecraft; - -/// -public class Settings : ISettings -{ - /// - public bool UseWireFrame { get; set; } - - /// - public bool PrintFrameRate { get; set; } - - /// - public RenderFlags RendererFlags { get; set; } = RenderFlags.All; - - /// - public WindowOptions WindowOptions { get; set; } -} diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index 8ffada1..dd4dabb 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -3,8 +3,11 @@ - net8.0-android;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041.0 + + 10.0.19041.0 + + net8.0-android;net8.0-ios;net8.0-maccatalyst + $(TargetFrameworks);net8.0-windows$(WindowsVersion) @@ -37,8 +40,10 @@ 14.0 24.0 10.0.17763.0 - 10.0.17763.0 + $(WindowsVersion) 6.5 + + Debug;Release;LauncherNonWindows @@ -69,10 +74,15 @@ - - + + + + + + - + + diff --git a/Launcher/MauiProgram.cs b/Launcher/MauiProgram.cs index 3d0393d..3fe0581 100644 --- a/Launcher/MauiProgram.cs +++ b/Launcher/MauiProgram.cs @@ -24,9 +24,8 @@ public static MauiApp CreateMauiApp() builder.Services.AddMauiBlazorWebView(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddSingleton(); + builder.Services.AddScoped(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); diff --git a/Launcher/Services/ApplicationManager.cs b/Launcher/Services/ApplicationManager.cs deleted file mode 100644 index 58e8c28..0000000 --- a/Launcher/Services/ApplicationManager.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Launcher.Services -{ - public interface IApplicationManager - { - void Close(); - } - - public class ApplicationManager : IApplicationManager - { - public void Close() - { - // TODO: Does not work - var window = (Application.Current as App)?.Handler?.MauiContext?.Services.GetService(); - window?.Close(); - } - } -} diff --git a/Launcher/Services/EditorService.cs b/Launcher/Services/EditorService.cs new file mode 100644 index 0000000..211adfe --- /dev/null +++ b/Launcher/Services/EditorService.cs @@ -0,0 +1,200 @@ +using Launcher.UI; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Launcher.Services +{ + /// + /// Contains definitions for interacting with the editor. + /// + public interface IEditorService + { + /// + /// Creates a new project and solution. + /// + /// The project to be created. + void Initialize(Project project); + + /// + /// Opens the specified project in the editor. + /// + /// The file to be opened. + /// + /// A representing an asynchronous operation. + /// The result of the operation contains the project that was loaded; if the project could not be loaded. + /// + Task LoadFileAsync(string filePath); + + /// + /// Loads the projects previously associated with the launcher. + /// + /// The projects file. + /// + /// All the found projects. + /// + List LoadProjects(string projectsFile); + + /// + /// Opens the specified project in the editor. + /// + /// + void OpenInEditor(Project project); + } + + /// + /// Handles interactions with the editor. + /// + public class EditorService : IEditorService + { + private const string SHARP_ENGINE_PROJECT_EXTENSION = "sharpproject"; + private const string SHARP_ENGINE_CORE_NUGET_PACKAGE = "SharpEngine.Core"; + private const string FRAMEWORK = "net8.0"; + + private readonly INotificationService _notificationService; + + /// + /// Initializes a new instance of . + /// + /// The notification message service used to display messages in the UI. + public EditorService(INotificationService notificationService) + { + _notificationService = notificationService; + } + + /// + public void Initialize(Project project) + { + CreateSharpEngineProject(project); + CreateSolution(project); + } + + private static void CreateSharpEngineProject(Project project) + { + var json = System.Text.Json.JsonSerializer.Serialize(project); + string projectFilePath = Path.Join(project.Path, $"{project.Name}.{SHARP_ENGINE_PROJECT_EXTENSION}"); + File.WriteAllText(projectFilePath, json); + } + + private void CreateSolution(Project project) + { + string projectName = project.Name!.Replace(" ", "_"); + + // Define individual commands + string createSolution = $"dotnet new sln -n {projectName} -o {project.Path}"; + string createProject = $"dotnet new console -n {projectName} -o {project.Path} -f {FRAMEWORK}"; + string addProjectToSolution = $"dotnet sln {project.Path}/{projectName}.sln add {project.Path}/{projectName}.csproj"; + + // TODO: Add the nuget package after it's been published + //string installNugetPackage = $"dotnet add {project.Path}/{projectName}.csproj package {SHARP_ENGINE_CORE_NUGET_PACKAGE}"; + string installNugetPackage = "echo done"; + + // Determine the correct argument string based on platform + string arguments = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + $"/c {createSolution} && {createProject} && {addProjectToSolution} && {installNugetPackage}" : + $"-c \"{createSolution}; {createProject}; {addProjectToSolution}; {installNugetPackage}\""; + + // Setup process + Process process = GetProcess(arguments); + process.Start(); + process.WaitForExit(); + + // Read and display output (optional) + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + if (!string.IsNullOrWhiteSpace(error)) + _notificationService.Show(error); + else + _notificationService.Show("Solution and project created successfully!"); + + // TODO: Set example programc.cs content with the minimal projects content. + // string programFilePath = Path.Combine(project.Path!, "Program.cs"); + // string programContent = ""; // TODO: Replace with program content + // + // File.WriteAllText(programFilePath, programContent); + } + + private static Process GetProcess(string arguments) + => new() + { + + StartInfo = new ProcessStartInfo + { + FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "/bin/sh", + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + /// + public void OpenInEditor(Project project) + { + if (!Path.Exists(project.Path)) + { + _notificationService.Show($"The project file '{project.Path}' no longer exists."); + return; + } + + try + { + const string editorPathEnvironmentVariable = "SHARPENGINE_EDITOR_PATH"; + string? editorPath = Environment.GetEnvironmentVariable(editorPathEnvironmentVariable); + if (editorPath is null) + { + _notificationService.Show("Editor variable not found"); + return; + } + + const string editorExecutable = "SharpEngine.Editor.exe"; + + string projectFile = project.Path.EndsWith(SHARP_ENGINE_PROJECT_EXTENSION) ? + project.Path : $"{project.Path}/{project.Name}.{SHARP_ENGINE_PROJECT_EXTENSION}"; + + var process = GetProcess($"{editorPath}/{editorExecutable} {projectFile}"); + process.Start(); + process.WaitForExit(); + } + catch (Exception ex) + { + _notificationService.Show($"An error occurred while opening the project in the editor.", false, ex.Message); + } + } + + /// + public List LoadProjects(string projectsFile) + { + if (!File.Exists(projectsFile)) + { + File.WriteAllText(projectsFile, "[]"); + return []; + } + + string json = File.ReadAllText(projectsFile); + var projects = System.Text.Json.JsonSerializer.Deserialize>(json); + + if (projects is not null) + return projects; + + _notificationService.Show($"Unable to load projects.", false, json, projects); + return []; + } + + /// + public async Task LoadFileAsync(string filePath) + { + if (!filePath.EndsWith(SHARP_ENGINE_PROJECT_EXTENSION, StringComparison.OrdinalIgnoreCase)) + { + _notificationService.Show($"The file '{filePath}' is not a valid project file."); + return null; + } + + using var reader = new StreamReader(filePath); + string fileContent = await reader.ReadToEndAsync(); + + return System.Text.Json.JsonSerializer.Deserialize(fileContent); + } + } +} diff --git a/Launcher/Services/NotificationService.cs b/Launcher/Services/NotificationService.cs index 3ccca92..f9b9185 100644 --- a/Launcher/Services/NotificationService.cs +++ b/Launcher/Services/NotificationService.cs @@ -1,15 +1,32 @@ -namespace Launcher.Services +using Serilog; +using Serilog.Core; + +namespace Launcher.Services { /// /// Contains definitions for displaying notifications in the UI. /// public interface INotificationService { + /// Gets or sets the notifications that are displayed in the UI. + Dictionary Notifications { get; set; } + + /// Sets the event executed when the notifications are changed. + void SetOnNotificationsChanged(Action action); + + /// + /// Discards a notification message from the UI. + /// + /// The message to be discarded. + void Discard(string message); + /// /// Displays a notification message in the UI. /// - /// - void Show(string message); + /// The message to be displayed. + /// Determines whether the message should be logged to a file. + /// The details that should be logged with the message. + void Show(string message, bool writeToFile = false, params object?[] details); } /// @@ -17,12 +34,51 @@ public interface INotificationService /// public class NotificationService : INotificationService { - // TODO: Dictionary to store the notification messages + /// + public Dictionary Notifications { get; set; } = new(); + + private event Action? OnNotificationsChanged; + private readonly Logger _log; + + /// + /// Initializes a new instance of . + /// + public NotificationService() + { + _log = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); + } + + /// + public void SetOnNotificationsChanged(Action action) + => OnNotificationsChanged = action; /// - public void Show(string message) + public void Show(string message, bool writeToFile = false, params object?[] details) { + if (writeToFile) + _log.Information(message, details); + if (Notifications.TryGetValue(message, out int count)) + Notifications[message] = count + 1; + else + Notifications.Add(message, 1); + + // Notify UI about the change + OnNotificationsChanged?.Invoke(); + } + + /// + public void Discard(string message) + { + if (!Notifications.TryGetValue(message, out int amount) || amount < 1) + return; + + if (amount == 1) + Notifications.Remove(message); + else + Notifications[message] = amount - 1; } } } diff --git a/Launcher/Services/ProjectInitializationService.cs b/Launcher/Services/ProjectInitializationService.cs deleted file mode 100644 index 1aba1e3..0000000 --- a/Launcher/Services/ProjectInitializationService.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Launcher.UI; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Launcher.Services -{ - public interface IProjectInitializationService - { - void Initialize(Project project); - } - - public class ProjectInitializationService : IProjectInitializationService - { - private const string SHARP_ENGINE_CORE_NUGET_PACKAGE = "SharpEngine.Core"; - private const string FRAMEWORK = "net8.0"; - - private readonly INotificationService _notificationService; - - /// - /// Initializes a new instance of . - /// - /// The notification message service used to display messages in the UI. - public ProjectInitializationService(INotificationService notificationService) - { - _notificationService = notificationService; - } - - /// - public void Initialize(Project project) - { - - CreateProjectDirectory(project); - CreateSharpEngineProject(project); - CreateSolution(project); - } - - private static void CreateProjectDirectory(Project project) - { - string projectDirectory = Path.GetDirectoryName(Path.GetFullPath(project.Path!))!; - Directory.CreateDirectory(projectDirectory); - } - - private static void CreateSharpEngineProject(Project project) - { - var json = System.Text.Json.JsonSerializer.Serialize(project); - File.WriteAllText(Path.Join(project.Path, $"{project.Name}.sharpproject"), json); - } - - private void CreateSolution(Project project) - { - string projectName = project.Name!.Replace(" ", "_"); - - // Define individual commands - string createSolution = $"dotnet new sln -n {projectName}"; - string createProject = $"dotnet new console -n {projectName} -o {project.Path} -f {FRAMEWORK}"; - string addProjectToSolution = $"dotnet sln {projectName}.sln add {project.Path}/{projectName}.csproj"; - - // TODO: Add the nuget package after it's been published - //string installNugetPackage = $"dotnet add {project.Path}/{projectName}.csproj package {SHARP_ENGINE_CORE_NUGET_PACKAGE}"; - string installNugetPackage = "echo done"; - - // Determine the correct argument string based on platform - string arguments = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - $"/c {createSolution} && {createProject} && {addProjectToSolution} && {installNugetPackage}" : - $"-c \"{createSolution}; {createProject}; {addProjectToSolution}; {installNugetPackage}\""; - - // Setup process - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "/bin/sh", - Arguments = arguments, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - } - }; - - process.Start(); - process.WaitForExit(); - - // Read and display output (optional) - string output = process.StandardOutput.ReadToEnd(); - string error = process.StandardError.ReadToEnd(); - - if (!string.IsNullOrWhiteSpace(error)) - _notificationService.Show(error); - - string programFilePath = Path.Combine(project.Path!, "Program.cs"); - string programContent = ""; // TODO: Replace with program content - - File.WriteAllText(programFilePath, programContent); - } - } -} diff --git a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor index ae3c60d..52ed7ad 100644 --- a/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor +++ b/Launcher/UI/Components/Dialogs/ConfirmDeleteDialog.razor @@ -3,7 +3,7 @@ - -@code { - private string _createProjectDialogVisibleClass => Visible ? "create-project-dialog-visible" : "create-project-dialog-hidden"; - - private EditContext _createProjectFormEditContext; - private Project _newProject; - - /// Gets or sets the event executed when the project is created. - [Parameter] - public EventCallback OnProjectCreated { get; set; } - - /// - /// Initializes a new instance of . - /// - public CreateProjectDialog() - { - _newProject = new(); - _createProjectFormEditContext = new(_newProject); - } - - /// - protected override void OnInitialized() - { - base.OnInitialized(); - - _createProjectFormEditContext = new(_newProject); - } - - private async Task OnCreateProjectSubmitClickedAsync() - { - await OnProjectCreated.InvokeAsync(_newProject); - - _newProject = new(); - await CloseAsync(); - } -} diff --git a/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs new file mode 100644 index 0000000..b4842e0 --- /dev/null +++ b/Launcher/UI/Components/Dialogs/CreateProjectDialog.razor.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Components; +using Launcher.Services; +using CommunityToolkit.Maui.Storage; + +namespace Launcher.UI.Components.Dialogs +{ + public partial class CreateProjectDialog : DialogBase + { + [Inject] + private INotificationService _notificationService { get; init; } = default!; + + private string _createProjectDialogVisibleClass => Visible ? "create-project-dialog-visible" : "create-project-dialog-hidden"; + private Project _newProject = new(); + + /// Gets or sets the event executed when the project is created. + [Parameter] + public EventCallback OnProjectCreated { get; set; } + + private async Task OnCreateProjectSubmitClickedAsync() + { + await OnProjectCreated.InvokeAsync(_newProject); + + _newProject = new(); + await CloseAsync(); + } + + private async Task HandleFileSelection() + { + var result = await FolderPicker.Default.PickAsync(); + + if (result is not null && result.IsSuccessful) + _newProject.Path = result.Folder.Path; + else + _notificationService.Show("No file selected.", false); + } + } +} diff --git a/Launcher/UI/Components/Dialogs/DialogBase.cs b/Launcher/UI/Components/Dialogs/DialogBase.cs index 4abe7fd..0af8ae4 100644 --- a/Launcher/UI/Components/Dialogs/DialogBase.cs +++ b/Launcher/UI/Components/Dialogs/DialogBase.cs @@ -22,21 +22,11 @@ public class DialogBase : ComponentBase /// /// Discards the dialog. /// - /// A representing an asyynchronous operation. + /// A representing an asynchronous operation. protected async Task CloseAsync() { Visible = false; await VisibleChanged.InvokeAsync(Visible); } - - /// - /// Discards the dialog. - /// - /// A representing an asyynchronous operation. - protected async Task DiscardAsync() - { - await OnDiscardClicked.InvokeAsync(); - await CloseAsync(); - } } } diff --git a/Launcher/UI/Components/Dialogs/Notification.razor b/Launcher/UI/Components/Dialogs/Notification.razor index aaeb912..4943980 100644 --- a/Launcher/UI/Components/Dialogs/Notification.razor +++ b/Launcher/UI/Components/Dialogs/Notification.razor @@ -1,18 +1,24 @@ -@inherits DialogBase +@using Launcher.Services +@inherits DialogBase - @@ -31,26 +23,9 @@ @context.Name @context.LastModified - + - - -@code { - // This method needs to be in the razor file due to the way the MAUI GetParentWindow method is exposed. - private void OpenInEditor(Project project) - { - if (!Path.Exists(project.Path)) - { - HandleMessage($"The project file '{project.Path}' no longer exists."); - return; - } - - // TODO: Close down the window when a valid project is loaded. - Process.Start("cmd"); - _applicationManager.Close(); - // Application.Current.CloseWindow(GetParentWindow()); - } -} \ No newline at end of file + \ No newline at end of file diff --git a/Launcher/UI/Home.razor.cs b/Launcher/UI/Home.razor.cs index 5674e39..e0e57a4 100644 --- a/Launcher/UI/Home.razor.cs +++ b/Launcher/UI/Home.razor.cs @@ -1,106 +1,46 @@ using Launcher.Services; using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Forms; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Launcher.UI { - public class Message - { - public readonly Guid Id = Guid.NewGuid(); - public string Contents { get; set; } = string.Empty; - - public Message(string contents) - { - Contents = contents; - } - } - /// Represents the home page of the launcher. public partial class Home : ComponentBase { [Inject] - private IProjectInitializationService _projectInitializationService { get; init; } = default!; + private IEditorService _editorService { get; init; } = default!; [Inject] private INotificationService _notificationService { get; init; } = default!; - [Inject] - private IApplicationManager _applicationManager { get; init; } = default!; - - private bool _showNotification; private bool _showConfirmDelete; private bool _showCreateProjectDialog; - // TODO: The notification messages should be added into a separate service. - private Dictionary _notificationMessages = new() - { - { new("some message"), 2 }, - { new("another"), 2 }, - }; - - private const string _projectsFile = "projects.json"; - private const string _launcherEnvironmentVariable = "SHARP_ENGINE_LAUNCHER_DIRECTORY"; - private readonly string _currentDirectory = AppContext.BaseDirectory; - private string _projectDirectory => Path.Join(_currentDirectory, _projectsFile); - - - private List Projects { get; set; } = new() - { - // TODO: Remove these mock projects once the actual projects are loaded. - new Project { Name = "Project 1", LastModified = DateTime.Now }, - new Project { Name = "Project 2", LastModified = DateTime.Now.AddDays(6).AddHours(2) }, - }; - + private List Projects { get; set; } = []; private Project _selectedProject = new(); + private const string _projectsFile = "projects.json"; + private static readonly string[] _sharpProjectFilePickerExtension = [".sharpproject"]; +#if DEBUG + private string _projectsFilePath = Path.Join(Path.GetTempPath(), _projectsFile); +#else + private string _projectsFilePath = Path.Join(AppContext.BaseDirectory, _projectsFile); +#endif /// - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - base.OnInitialized(); + await base.OnInitializedAsync(); // TODO: Get current launcher version - // TODO: Check if GitHub has a new version released. - CheckEnvironmentVariable(); - // LoadProjects(); - } - - private void CheckEnvironmentVariable() - { - var launcherDirectory = Environment.GetEnvironmentVariable(_launcherEnvironmentVariable); - if (string.IsNullOrEmpty(launcherDirectory) || launcherDirectory != _currentDirectory) - Environment.SetEnvironmentVariable(_launcherEnvironmentVariable, _currentDirectory); - } - - private void LoadProjects() - { - if (!File.Exists(_projectDirectory)) - File.WriteAllText(_projectDirectory, "[]"); - - string json = File.ReadAllText(_projectDirectory); - var projects = System.Text.Json.JsonSerializer.Deserialize>(json); - + var projects = _editorService.LoadProjects(_projectsFilePath); + if (projects is not null) - Projects = projects; - else - HandleMessage($"Unable to load projects.", json, projects); - } - - private async Task LoadFileAsync(InputFileChangeEventArgs e) - { - // TODO: Check if file is a "sharpproject.json" file - - using var stream = e.File.OpenReadStream(); - using var reader = new StreamReader(stream); - string fileContent = await reader.ReadToEndAsync(); - - var project = System.Text.Json.JsonSerializer.Deserialize(fileContent); - - if (project is not null) - Projects.Add(project); + Projects.AddRange(projects); else - HandleMessage($"Unable to load project file.", project, e.File.Name, fileContent); + _notificationService.Show($"Unable to load project file.", false, projects); } private void OnCreateProjectClicked() @@ -115,28 +55,60 @@ private void OnDeleteProjectClicked(Project project) private void OnDeleteProjectConfirm() { var project = Projects.Find(p => p.Id == _selectedProject.Id); - if (project is not null) + { Projects.Remove(project); + // TODO: Delete project files + } else - HandleMessage($"Unable to delete project '{project?.Name}'.", _selectedProject, Projects); + _notificationService.Show($"Unable to delete project '{project?.Name}'.", false, _selectedProject, Projects); } private void ProjectCreated(Project project) { - _projectInitializationService.Initialize(project); - Projects.Add(project); + _editorService.Initialize(project); + SetProjects(project); + } + + private async Task OpenExistingFileAsync() + { + var pickedFile = await FilePicker.Default.PickAsync(new PickOptions + { + PickerTitle = "Select a SharpProject file", + FileTypes = new FilePickerFileType(new Dictionary> + { + { DevicePlatform.WinUI, _sharpProjectFilePickerExtension }, + { DevicePlatform.MacCatalyst, _sharpProjectFilePickerExtension }, + { DevicePlatform.iOS, _sharpProjectFilePickerExtension }, + { DevicePlatform.Android, new[] { "application/octet-stream" } } + }) + }); + + if (pickedFile?.FileName.EndsWith(".sharpproject") == false) + { + _notificationService.Show("Invalid file type selected. Please select a .sharpproject file."); + return; + } + + var project = await _editorService.LoadFileAsync(pickedFile!.FullPath); + if (project is not null) + SetProjects(project); + else + _notificationService.Show($"Unable to load project file '{pickedFile.FullPath}'"); } - private void HandleMessage(string message, params object?[] details) + private void SetProjects(Project project) { - // TODO: Get the message using the Id - // if (!_notificationMessages.TryGetValue(message, out int amount)) - _notificationMessages.Add(new Message(message), 1); - // else - // _notificationMessages[message] = amount + 1; + bool projectAlreadyExists = Projects.Any(p => p.Path == project.Path || p.Name == project.Name); + if (projectAlreadyExists) + { + _notificationService.Show($"Project '{project.Name}' already exists.", false, project); + return; + } - // TODO: Log the message and details. + Projects.Add(project); + string json = JsonSerializer.Serialize(Projects); + File.WriteAllText(_projectsFilePath, json); } } } diff --git a/Launcher/UI/Project.cs b/Launcher/UI/Project.cs index f56f3c5..bbdac82 100644 --- a/Launcher/UI/Project.cs +++ b/Launcher/UI/Project.cs @@ -24,12 +24,11 @@ public class Project /// Gets or sets the path to the project file. /// [Required] - [File] public string? Path { get; set; } /// /// Gets or sets when the project was last modified. /// [JsonIgnore] - public DateTime LastModified { get; set; } + public DateTime LastModified { get; set; } = DateTime.Now; } diff --git a/Launcher/UI/_Imports.razor b/Launcher/UI/_Imports.razor index ac1123b..464f868 100644 --- a/Launcher/UI/_Imports.razor +++ b/Launcher/UI/_Imports.razor @@ -8,4 +8,5 @@ @using Microsoft.JSInterop @using Launcher -@using Launcher.UI \ No newline at end of file +@using Launcher.UI +@using Launcher.Services \ No newline at end of file diff --git a/Launcher/compilerconfig.json b/Launcher/compilerconfig.json index 0b02419..9fe060d 100644 --- a/Launcher/compilerconfig.json +++ b/Launcher/compilerconfig.json @@ -18,5 +18,9 @@ { "outputFile": "wwwroot/css/additional.css", "inputFile": "wwwroot/css/additional.scss" + }, + { + "outputFile": "UI/Components/Dialogs/ConfirmDeleteDialog.razor.css", + "inputFile": "UI/Components/Dialogs/ConfirmDeleteDialog.razor.scss" } ] \ No newline at end of file diff --git a/Launcher/wwwroot/index.html b/Launcher/wwwroot/index.html index 15dd33a..0adf935 100644 --- a/Launcher/wwwroot/index.html +++ b/Launcher/wwwroot/index.html @@ -12,8 +12,6 @@ - - diff --git a/Launcher/wwwroot/js/filePicker.js b/Launcher/wwwroot/js/filePicker.js deleted file mode 100644 index 43518c0..0000000 --- a/Launcher/wwwroot/js/filePicker.js +++ /dev/null @@ -1,3 +0,0 @@ -window.triggerFileInput = function (id) { - document.getElementById(id).click(); -}; \ No newline at end of file diff --git a/SharpEngine.sln b/SharpEngine.sln new file mode 100644 index 0000000..f56a679 --- /dev/null +++ b/SharpEngine.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher", "Launcher\Launcher.csproj", "{3751B661-BE0E-9CD7-CA51-361A34450372}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minimal", "Examples\Minimal\Minimal.csproj", "{1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Editor", "Editor\SharpEngine.Editor.csproj", "{F72A74DC-54F9-0C76-2D40-32D2524683F8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEngine.Core.NET", "Core\SharpEngine.Core.NET.csproj", "{6B242114-B3AC-D598-A74D-D84B64702471}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minecraft", "Examples\MinecraftClone\Minecraft.csproj", "{E8A75C3B-1D82-8B30-C854-AD35FB123CDC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.Build.0 = Release|Any CPU + {3751B661-BE0E-9CD7-CA51-361A34450372}.Release|Any CPU.Deploy.0 = Release|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E}.Release|Any CPU.Build.0 = Release|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F72A74DC-54F9-0C76-2D40-32D2524683F8}.Release|Any CPU.Build.0 = Release|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B242114-B3AC-D598-A74D-D84B64702471}.Release|Any CPU.Build.0 = Release|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1BEC06D8-094D-8BC2-288E-E2C4C13F3D2E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {E8A75C3B-1D82-8B30-C854-AD35FB123CDC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {78D50298-A91B-44A5-A78E-4858134662B2} + EndGlobalSection +EndGlobal diff --git a/SharpEngine.slnx b/SharpEngine.slnx new file mode 100644 index 0000000..4815b93 --- /dev/null +++ b/SharpEngine.slnx @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +