diff --git a/.gitmoji-changelogrc b/.gitmoji-changelogrc index e638cba..fe4a667 100644 --- a/.gitmoji-changelogrc +++ b/.gitmoji-changelogrc @@ -2,7 +2,7 @@ "project": { "name": "quartz", "description": "An Integrated Development Environment for creating Battler-Based RPG and Dungeon Crawler games.", - "version": "1.1.5-alpha" + "version": "1.2.1-alpha" }, "commitMapping": [ { diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b48ae..4a8ab9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,83 @@ # Changelog + +## 1.2.1-alpha (2020-09-28) + +### Optimization + +- ♻️ Change synchronous RelayCommands to asynchronous ReactiveCommands [[f69858d](https://github.com/TheHeadmaster/Quartz/commit/f69858dddda73b01796c0fe04a49a44dcf68b15e)] +- ♻️ Remove and refactor a metric shit-ton of boilerplate code [[69f7e03](https://github.com/TheHeadmaster/Quartz/commit/69f7e031df2d2e90c584ce510abbbd96549cc7f2)] +- ♻️ Preferences now save to the user's AppData directory instead of directly inside the Program Files folder [[609d511](https://github.com/TheHeadmaster/Quartz/commit/609d511f626c70ab2d7946ff75e4dfb2961d09ca)] +- ♻️ Move Logs output directory from Program Files to AppData [[7dc0c43](https://github.com/TheHeadmaster/Quartz/commit/7dc0c43e3ca19f4d4997224a42cef9cc6ac9c099)] +- ♻️ Common functionality between Quartz.IDE and Quartz.Engine moved to Quartz.Core [[be3afff](https://github.com/TheHeadmaster/Quartz/commit/be3afff9a840c2c8663ab2039691c4674fd83e95)] + +### Breaking and Major Changes + +- 🏗️ Templates are finally here. Now you can create and use Core, UI, and Pack templates to build a project from so you don't have to start from scratch. [[f7075f7](https://github.com/TheHeadmaster/Quartz/commit/f7075f7dc2219f3ded8b4b35f26fe17120fff284)] +- 🏗️ Add ability to compile and run Mono projects [[87c4356](https://github.com/TheHeadmaster/Quartz/commit/87c43566024c793bd0dcaad57c748b49279e075a)] +- 💥 Project files are now in Database format instead of Json file format [[764803c](https://github.com/TheHeadmaster/Quartz/commit/764803cd13508fdbf1c89a5ae1b9419eb5228a5c)] +- 💥 Previous projects are now incompatible moving forward, starting with 1.2.1 [[6fafdf3](https://github.com/TheHeadmaster/Quartz/commit/6fafdf35d357ef65683dd6c16e97b9a1e112430d)] +- 🏗️ Add Quartz.Testing Unit Test Project [[7f867f4](https://github.com/TheHeadmaster/Quartz/commit/7f867f47734ee21e6db395c950ce515f0f5eff9e)] +- 🏗️ Add Quartz.Engine Library [[e764051](https://github.com/TheHeadmaster/Quartz/commit/e764051cf313a714ae3cac8cbd2672f128e4cb04)] +- 🚀 Change Quartz Editor to Quartz IDE [[fdfb673](https://github.com/TheHeadmaster/Quartz/commit/fdfb673aefe93252bb2d21b84cffbbd41840998f)] + +### Bug Fixes + +- 🐛 Selecting No to opening a new project while an existing unsaved project was open still opened the project and didn't save. Press F to pay respects [[46dba05](https://github.com/TheHeadmaster/Quartz/commit/46dba05e9d7c4a9bfc1c79ae66cf9dd5e307a3b2)] + +### New Features + +- ✨ Add calculated Element offensive and defensive ratings [[a73bf94](https://github.com/TheHeadmaster/Quartz/commit/a73bf94d86ed8e503072bf79bb338fbdeb51f896)] +- ✨ Add the Changelog to the About window [[9b8d52c](https://github.com/TheHeadmaster/Quartz/commit/9b8d52ce89fcb393b3f17dca50b1b57b996f0a21)] +- ✨ Add elements and definable strengths, weaknesses, and immunities [[5e5bcd2](https://github.com/TheHeadmaster/Quartz/commit/5e5bcd27d40b1b867e7b939d331c18cd6e25bc5b)] +- ✨ Add backwards compatibility check for older projects, and versioning for projects moving forward [[03b4966](https://github.com/TheHeadmaster/Quartz/commit/03b4966b2d30ac6139d5602ac518228d2c222722)] + +### Diagnostics + +- ⚗️ Add method-level logging and error catching utilizing Serilog and MethodBoundaryAspect [[ab97e2f](https://github.com/TheHeadmaster/Quartz/commit/ab97e2fa8e178bd3f8e95c4ea2668948ebfdaf50)] + +### Dependency Changes + +- ⬆️ Update PersistentEntity 1.0.2 ➡️ 1.0.3 [[22f5c18](https://github.com/TheHeadmaster/Quartz/commit/22f5c183d9a0ca7f4d5c34e24701eb6e3c8a5967)] +- ⬆️ Update MethodBoundaryAspect 2.0.122 ➡️ 2.0.123 [[6a8ffae](https://github.com/TheHeadmaster/Quartz/commit/6a8ffaec813749a9a1d2214bcb442425f3e81c3f)] +- ⬆️ Update PersistentEntity 1.0.1 ➡️ 1.0.2 [[2481663](https://github.com/TheHeadmaster/Quartz/commit/2481663e3397574b48c17964a27b9350e77b30c8)] +- ⬆️ Update ReactiveProperty 7.4.1 ➡️ 7.4.1 [[927872d](https://github.com/TheHeadmaster/Quartz/commit/927872dfa0cd8eb6528ab37aabe21036faa1784b)] +- ⬆️ Update PersistentEntity 1.0.0 ➡️ 1.0.1 [[9b7930b](https://github.com/TheHeadmaster/Quartz/commit/9b7930bab856504779dbe6c7828f5b9854fcb01d)] +- ➕ Add Entity Framework Core Analyzers [[606a95a](https://github.com/TheHeadmaster/Quartz/commit/606a95afb01bb253807a1fc6f5d27722e144df3d)] +- ➕ Add Entity Framework Core Abstractions Database Library [[09eece9](https://github.com/TheHeadmaster/Quartz/commit/09eece987a7a104966101c1e05fdc1ae1b9fe1a1)] +- ➕ Add Entity Framework Core Relational Database Library [[50dd33d](https://github.com/TheHeadmaster/Quartz/commit/50dd33dd54a80d285005ecc8cd3e221c4299a7f6)] +- ➕ Add Entity Framework Core Tools Database Library [[5fff933](https://github.com/TheHeadmaster/Quartz/commit/5fff93334a827dccf4533a8da99431f3c5fd1605)] +- ➕ Add Entity Framework Core Database Library [[061e05f](https://github.com/TheHeadmaster/Quartz/commit/061e05f58767b9fc545a9bb51b0c8cfcc9f39005)] +- ➕ Add Microsoft.NET.HostModel Compiler Library [[b3f1780](https://github.com/TheHeadmaster/Quartz/commit/b3f1780691654be8e726010401b9e032a20ca65b)] +- ➕ Add Microsoft.Extensions.DependencyModel Compiler Library [[c7cdb35](https://github.com/TheHeadmaster/Quartz/commit/c7cdb35f54fa4ff87d63b10cfa4a49487bee94d4)] +- ➕ Add Microsoft.DotNet.PlatformAbstractions Compiler Library [[6161fb5](https://github.com/TheHeadmaster/Quartz/commit/6161fb5ca76ff4427981196c17dbd2f61c62c85b)] +- ➕ Add Microsoft.CodeAnalysis.Workspaces.MSBuild Compiler Library [[3d38157](https://github.com/TheHeadmaster/Quartz/commit/3d3815736752a74a9ebdbd698952fd54dbfb32f1)] +- ➕ Add Microsoft.CodeAnalysis.CSharp.Workspaces Compiler Library [[31525e4](https://github.com/TheHeadmaster/Quartz/commit/31525e438f4983bae0eaefcfb5d6b385cd9ab32e)] +- ➕ Add Microsoft.CodeAnalysis Compiler Library [[163612c](https://github.com/TheHeadmaster/Quartz/commit/163612c5a493c5ce9155937661140faa490f7cdd)] +- ➕ Add Microsoft.Build.Tasks.Core Compiler Library [[799113b](https://github.com/TheHeadmaster/Quartz/commit/799113b98be17deae5b2801aae314b317083593c)] +- ➕ Add Microsoft.Build.Locator Compiler Library [[7a10c60](https://github.com/TheHeadmaster/Quartz/commit/7a10c608ced68ff2e40f6743ef19254807de8683)] +- ➕ Add Microsoft.Build.Framework Compiler Library [[376c473](https://github.com/TheHeadmaster/Quartz/commit/376c4732c627cbcde6ed34c6957cdbca92411f49)] +- ➕ Add Microsoft.Build.Runtime Compiler Library [[51efa89](https://github.com/TheHeadmaster/Quartz/commit/51efa8993dbe3b1bef5f22f96939f82c312956ba)] +- ➕ Add Microsoft.Build.Engine Compiler Library [[7c72459](https://github.com/TheHeadmaster/Quartz/commit/7c724592a0fe3cd0a6c886e1f125c7a4cefb1d56)] +- ➕ Add Microsoft.Build Compiler Library [[29d06f6](https://github.com/TheHeadmaster/Quartz/commit/29d06f678ec5271a88c9d211a69012e517b58c76)] +- ⬆️ Update Librarium 1.0.2 ➡️ 1.0.3 [[27e4ffc](https://github.com/TheHeadmaster/Quartz/commit/27e4ffcc71830e594aa5a2529ce5dd3dee1a12b6)] +- ➕ Add PersistentEntity Database Library [[f16ceb7](https://github.com/TheHeadmaster/Quartz/commit/f16ceb7bde0954c33f627831c16f6c87ba151b88)] +- ➕ Add FluentAssertions Testing Library [[f42ffa1](https://github.com/TheHeadmaster/Quartz/commit/f42ffa1550bf1ba14d7b1ac9ecfacba9a8ca3830)] +- ➕ Add XBehave Testing Library [[6ca9c8b](https://github.com/TheHeadmaster/Quartz/commit/6ca9c8b305edfdb966823104807d9d540038e962)] +- ⬆️ Update XUnit Visual Studio Runner 2.4.0 ➡️ 2.4.3 [[0506e07](https://github.com/TheHeadmaster/Quartz/commit/0506e0758e62a0abd8e43f323a664e340701e7e6)] +- ⬆️ Update XUnit 2.4.0 ➡️ 2.4.1 [[a9826d3](https://github.com/TheHeadmaster/Quartz/commit/a9826d378a2ae457e815b44e6b972d584de1339d)] +- ⬆️ Update .NET Test SDK 16.5.0 ➡️ 16.7.1 [[0d66e8d](https://github.com/TheHeadmaster/Quartz/commit/0d66e8dad08363e5c41107ac9d2f022f109a08d5)] +- ⬆️ Update Coverlet.Collector 1.2.0 ➡️ 1.3.0 [[f6c6c0c](https://github.com/TheHeadmaster/Quartz/commit/f6c6c0caea858f679fe6aeee95334c65ec24954d)] +- ➕ Add Serilog Analyzers [[560d15c](https://github.com/TheHeadmaster/Quartz/commit/560d15c506ff41e7fe6e4a923c1be285010069e6)] +- ➕ Add Serilog Compact Formatting Logging Library [[4c036a9](https://github.com/TheHeadmaster/Quartz/commit/4c036a987785600d983c506644ba4ba0f520e127)] +- ➕ Add Serilog Exception Logging Library [[355b7ad](https://github.com/TheHeadmaster/Quartz/commit/355b7ad47ede98a1736197d57193db7b3d32d0cc)] +- ➕ Add Serilog Seq Sink Library [[87a8b9f](https://github.com/TheHeadmaster/Quartz/commit/87a8b9fb8f50791b19b0f8fd160192c5e7e53137)] +- ➕ Add Serilog File Sink Library [[c913daa](https://github.com/TheHeadmaster/Quartz/commit/c913daa61ce5eeb4bd126adfd2285980d0348c28)] +- ➕ Add Serilog Console Sink Library [[3edad91](https://github.com/TheHeadmaster/Quartz/commit/3edad910758b4cde528eda27a3e6abd50c863d05)] +- ➕ Add Serilog Debug Sink Library [[49a95ba](https://github.com/TheHeadmaster/Quartz/commit/49a95ba462761db9ce5101b9426d4bd35fd659f3)] +- ➕ Add Serilog Logging Library [[48b5bcf](https://github.com/TheHeadmaster/Quartz/commit/48b5bcf6780bd0d3c4f9e2c7c33f755765094e21)] +- ⬆️ Upgrade Librarium and Librarium.WPF to Nuget package [[4b82008](https://github.com/TheHeadmaster/Quartz/commit/4b82008d5f40c0ad3f0f74c9dc17846b17d42ffe)] + ## 1.1.6-alpha (2020-09-20) diff --git a/Quartz.Core/Class1.cs b/Quartz.Core/Class1.cs deleted file mode 100644 index cb486f3..0000000 --- a/Quartz.Core/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Quartz.Core -{ - public class Class1 - { - } -} diff --git a/Quartz.Core/Diagnostics/LogAttribute.cs b/Quartz.Core/Diagnostics/LogAttribute.cs new file mode 100644 index 0000000..6039ffb --- /dev/null +++ b/Quartz.Core/Diagnostics/LogAttribute.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Librarium.Core; +using MethodBoundaryAspect.Fody.Attributes; +using Serilog; + +namespace Quartz.Core.Diagnostics +{ + /// + /// Logs a message to the sink pool before and/or after a method call. + /// + public sealed class LogAttribute : OnMethodBoundaryAspect + { + /// + /// The message to log before the method is called. + /// + public string EntryMessage { get; private set; } + + /// + /// The message to log if the method throws an exception. + /// + public string ExceptionMessage { get; private set; } + + /// + /// The message to log after a method is called. + /// + public string ExitMessage { get; private set; } + + /// + /// Determines whether the thrown exception should be considered a fatal exception. + /// + public bool IsExceptionFatal { get; private set; } + + /// + /// Logs a message to the sink pool before and/or after a method call. + /// + /// + /// The message to log before the method is called. + /// + /// + /// The message to log after a method is completed. + /// + /// + /// The message to log in the event that the method throws an exception. + /// + /// + /// Tells the logger that any exception thrown in this method is considered a fatal + /// exception, and should be logged as such. + /// + public LogAttribute(string entryMessage = "", string exitMessage = "", string exceptionMessage = "", bool IsExceptionFatal = false) + { + this.EntryMessage = entryMessage; + this.ExitMessage = exitMessage; + this.ExceptionMessage = exceptionMessage; + this.IsExceptionFatal = IsExceptionFatal; + } + + public override void OnEntry(MethodExecutionArgs args) + { + if (!this.EntryMessage.IsNullOrWhiteSpace()) + { + Log.Information("{EntryMessage}", this.EntryMessage); + } + } + + public override void OnException(MethodExecutionArgs args) + { + if (this.IsExceptionFatal) + { + if (!this.ExceptionMessage.IsNullOrWhiteSpace()) + { + Log.Fatal(args.Exception, ""); + } + else + { + Log.Fatal(args.Exception, "{Exception}", this.ExceptionMessage); + } + } + else + { + if (!this.ExceptionMessage.IsNullOrWhiteSpace()) + { + Log.Error(args.Exception, ""); + } + else + { + Log.Error(args.Exception, "{Exception}", this.ExceptionMessage); + } + } + } + + public override void OnExit(MethodExecutionArgs args) + { + if (args.ReturnValue is Task t) + { + t.ContinueWith(task => + { + if (!this.ExitMessage.IsNullOrWhiteSpace()) + { + Log.Information("{ExitMessage}", this.ExitMessage); + } + }); + } + else + { + if (!this.ExitMessage.IsNullOrWhiteSpace()) + { + Log.Information("{ExitMessage}", this.ExitMessage); + } + } + } + } +} \ No newline at end of file diff --git a/Quartz.Core/Diagnostics/LogManager.cs b/Quartz.Core/Diagnostics/LogManager.cs new file mode 100644 index 0000000..e2e9ecf --- /dev/null +++ b/Quartz.Core/Diagnostics/LogManager.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Librarium.Core; +using Serilog; +using Serilog.Formatting.Compact; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Quartz.Core.Diagnostics +{ + /// + /// Manages logging setup and functions. + /// + public static class LogManager + { + private static string logPath = ""; + + /// + /// Sets up the color theme for ESDashboard. + /// + private static AnsiConsoleTheme QuartzTheme { get; } = new AnsiConsoleTheme( + new Dictionary + { + [ConsoleThemeStyle.Text] = "\x1b[38;5;0229m", + [ConsoleThemeStyle.SecondaryText] = "\x1b[38;5;0246m", + [ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0242m", + [ConsoleThemeStyle.Invalid] = "\x1b[33;1m", + [ConsoleThemeStyle.Null] = "\x1b[38;5;0038m", + [ConsoleThemeStyle.Name] = "\x1b[38;5;0081m", + [ConsoleThemeStyle.String] = "\x1b[38;5;0216m", + [ConsoleThemeStyle.Number] = "\x1b[38;5;151m", + [ConsoleThemeStyle.Boolean] = "\x1b[38;5;0038m", + [ConsoleThemeStyle.Scalar] = "\x1b[38;5;0079m", + [ConsoleThemeStyle.LevelVerbose] = "\x1b[197m", + [ConsoleThemeStyle.LevelDebug] = "\x1b[089m", + [ConsoleThemeStyle.LevelInformation] = "\x1b[37;163m", + [ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0226m", + [ConsoleThemeStyle.LevelError] = "\x1b[38;5;0160m", + [ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0124m", + }); + + /// + /// Initializes the log manager. Logging sinks are set up here. + /// + public static void Initialize() + { + if (logPath.IsNullOrWhiteSpace()) + { + DateTime dt = DateTime.UtcNow; + string filename = $"{dt.Month}-{dt.Day}-{dt.Year} {dt.Hour}-{dt.Minute}-{dt.Second}"; + logPath = Path.Combine(AppMeta.AppDataDirectory, "Quartz", "Logs", $"{filename}.json"); + } + if (!Directory.Exists(Path.Combine(AppMeta.AppDataDirectory, "Quartz", "Logs"))) + { + Directory.CreateDirectory(Path.Combine(AppMeta.AppDataDirectory, "Quartz", "Logs")); + } + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {Message:lj}{NewLine}{Exception}", theme: QuartzTheme) + .WriteTo.Debug(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Seq("http://localhost:5341") + .WriteTo.File(new CompactJsonFormatter(), logPath) + .CreateLogger(); + + Log.Information("Logging to the output tab."); + Log.Information("Logging to Seq. Open http://localhost:5341 for more details."); + Log.Information("Logging to compact json file. Check the 'Logs' folder."); + } + } +} \ No newline at end of file diff --git a/Quartz.Core/FodyWeavers.xml b/Quartz.Core/FodyWeavers.xml new file mode 100644 index 0000000..d510074 --- /dev/null +++ b/Quartz.Core/FodyWeavers.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Quartz.Core/FodyWeavers.xsd b/Quartz.Core/FodyWeavers.xsd new file mode 100644 index 0000000..a097b30 --- /dev/null +++ b/Quartz.Core/FodyWeavers.xsd @@ -0,0 +1,76 @@ + + + + + + + + + + + + Used to control if the On_PropertyName_Changed feature is enabled. + + + + + Used to control if the Dependent properties feature is enabled. + + + + + Used to control if the IsChanged property feature is enabled. + + + + + Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. + + + + + Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. + + + + + Used to control if equality checks should use the Equals method resolved from the base class. + + + + + Used to control if equality checks should use the static Equals method resolved from the base class. + + + + + Used to turn off build warnings from this weaver. + + + + + Used to turn off build warnings about mismatched On_PropertyName_Changed methods. + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/Quartz.Core/Migrations/20200926063719_Initial.Designer.cs b/Quartz.Core/Migrations/20200926063719_Initial.Designer.cs new file mode 100644 index 0000000..d0e7449 --- /dev/null +++ b/Quartz.Core/Migrations/20200926063719_Initial.Designer.cs @@ -0,0 +1,91 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Quartz.Core.ObjectModel; + +namespace Quartz.Core.Migrations +{ + [DbContext(typeof(QuartzContext))] + [Migration("20200926063719_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Quartz.Core.ObjectModel.Element", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.ToTable("Elements"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttackingElementID") + .HasColumnType("int"); + + b.Property("DefendingElementID") + .HasColumnType("int"); + + b.Property("Multiplier") + .HasColumnType("float"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.HasIndex("AttackingElementID"); + + b.HasIndex("DefendingElementID"); + + b.ToTable("ElementMatchup"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.HasOne("Quartz.Core.ObjectModel.Element", "AttackingElement") + .WithMany("ElementMatchups") + .HasForeignKey("AttackingElementID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Quartz.Core.ObjectModel.Element", "DefendingElement") + .WithMany() + .HasForeignKey("DefendingElementID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Quartz.Core/Migrations/20200926063719_Initial.cs b/Quartz.Core/Migrations/20200926063719_Initial.cs new file mode 100644 index 0000000..665c5db --- /dev/null +++ b/Quartz.Core/Migrations/20200926063719_Initial.cs @@ -0,0 +1,72 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Quartz.Core.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Elements", + columns: table => new + { + ID = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RowVersion = table.Column(rowVersion: true, nullable: true), + Name = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Elements", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "ElementMatchup", + columns: table => new + { + ID = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RowVersion = table.Column(rowVersion: true, nullable: true), + AttackingElementID = table.Column(nullable: false), + DefendingElementID = table.Column(nullable: false), + Multiplier = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ElementMatchup", x => x.ID); + table.ForeignKey( + name: "FK_ElementMatchup_Elements_AttackingElementID", + column: x => x.AttackingElementID, + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ElementMatchup_Elements_DefendingElementID", + column: x => x.DefendingElementID, + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_ElementMatchup_AttackingElementID", + table: "ElementMatchup", + column: "AttackingElementID"); + + migrationBuilder.CreateIndex( + name: "IX_ElementMatchup_DefendingElementID", + table: "ElementMatchup", + column: "DefendingElementID"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ElementMatchup"); + + migrationBuilder.DropTable( + name: "Elements"); + } + } +} diff --git a/Quartz.Core/Migrations/20200926185759_ElementMatchup.Designer.cs b/Quartz.Core/Migrations/20200926185759_ElementMatchup.Designer.cs new file mode 100644 index 0000000..673bf64 --- /dev/null +++ b/Quartz.Core/Migrations/20200926185759_ElementMatchup.Designer.cs @@ -0,0 +1,91 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Quartz.Core.ObjectModel; + +namespace Quartz.Core.Migrations +{ + [DbContext(typeof(QuartzContext))] + [Migration("20200926185759_ElementMatchup")] + partial class ElementMatchup + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Quartz.Core.ObjectModel.Element", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.ToTable("Elements"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttackingElementID") + .HasColumnType("int"); + + b.Property("DefendingElementID") + .HasColumnType("int"); + + b.Property("Multiplier") + .HasColumnType("float"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.HasIndex("AttackingElementID"); + + b.HasIndex("DefendingElementID"); + + b.ToTable("ElementMatchups"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.HasOne("Quartz.Core.ObjectModel.Element", "AttackingElement") + .WithMany("ElementMatchups") + .HasForeignKey("AttackingElementID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Quartz.Core.ObjectModel.Element", "DefendingElement") + .WithMany() + .HasForeignKey("DefendingElementID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Quartz.Core/Migrations/20200926185759_ElementMatchup.cs b/Quartz.Core/Migrations/20200926185759_ElementMatchup.cs new file mode 100644 index 0000000..cc011ad --- /dev/null +++ b/Quartz.Core/Migrations/20200926185759_ElementMatchup.cs @@ -0,0 +1,107 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Quartz.Core.Migrations +{ + public partial class ElementMatchup : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ElementMatchup_Elements_AttackingElementID", + table: "ElementMatchup"); + + migrationBuilder.DropForeignKey( + name: "FK_ElementMatchup_Elements_DefendingElementID", + table: "ElementMatchup"); + + migrationBuilder.DropPrimaryKey( + name: "PK_ElementMatchup", + table: "ElementMatchup"); + + migrationBuilder.RenameTable( + name: "ElementMatchup", + newName: "ElementMatchups"); + + migrationBuilder.RenameIndex( + name: "IX_ElementMatchup_DefendingElementID", + table: "ElementMatchups", + newName: "IX_ElementMatchups_DefendingElementID"); + + migrationBuilder.RenameIndex( + name: "IX_ElementMatchup_AttackingElementID", + table: "ElementMatchups", + newName: "IX_ElementMatchups_AttackingElementID"); + + migrationBuilder.AddPrimaryKey( + name: "PK_ElementMatchups", + table: "ElementMatchups", + column: "ID"); + + migrationBuilder.AddForeignKey( + name: "FK_ElementMatchups_Elements_AttackingElementID", + table: "ElementMatchups", + column: "AttackingElementID", + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_ElementMatchups_Elements_DefendingElementID", + table: "ElementMatchups", + column: "DefendingElementID", + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ElementMatchups_Elements_AttackingElementID", + table: "ElementMatchups"); + + migrationBuilder.DropForeignKey( + name: "FK_ElementMatchups_Elements_DefendingElementID", + table: "ElementMatchups"); + + migrationBuilder.DropPrimaryKey( + name: "PK_ElementMatchups", + table: "ElementMatchups"); + + migrationBuilder.RenameTable( + name: "ElementMatchups", + newName: "ElementMatchup"); + + migrationBuilder.RenameIndex( + name: "IX_ElementMatchups_DefendingElementID", + table: "ElementMatchup", + newName: "IX_ElementMatchup_DefendingElementID"); + + migrationBuilder.RenameIndex( + name: "IX_ElementMatchups_AttackingElementID", + table: "ElementMatchup", + newName: "IX_ElementMatchup_AttackingElementID"); + + migrationBuilder.AddPrimaryKey( + name: "PK_ElementMatchup", + table: "ElementMatchup", + column: "ID"); + + migrationBuilder.AddForeignKey( + name: "FK_ElementMatchup_Elements_AttackingElementID", + table: "ElementMatchup", + column: "AttackingElementID", + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_ElementMatchup_Elements_DefendingElementID", + table: "ElementMatchup", + column: "DefendingElementID", + principalTable: "Elements", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/Quartz.Core/Migrations/QuartzContextModelSnapshot.cs b/Quartz.Core/Migrations/QuartzContextModelSnapshot.cs new file mode 100644 index 0000000..54d703d --- /dev/null +++ b/Quartz.Core/Migrations/QuartzContextModelSnapshot.cs @@ -0,0 +1,89 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Quartz.Core.ObjectModel; + +namespace Quartz.Core.Migrations +{ + [DbContext(typeof(QuartzContext))] + partial class QuartzContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Quartz.Core.ObjectModel.Element", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.ToTable("Elements"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AttackingElementID") + .HasColumnType("int"); + + b.Property("DefendingElementID") + .HasColumnType("int"); + + b.Property("Multiplier") + .HasColumnType("float"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("ID"); + + b.HasIndex("AttackingElementID"); + + b.HasIndex("DefendingElementID"); + + b.ToTable("ElementMatchups"); + }); + + modelBuilder.Entity("Quartz.Core.ObjectModel.ElementMatchup", b => + { + b.HasOne("Quartz.Core.ObjectModel.Element", "AttackingElement") + .WithMany("ElementMatchups") + .HasForeignKey("AttackingElementID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Quartz.Core.ObjectModel.Element", "DefendingElement") + .WithMany() + .HasForeignKey("DefendingElementID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Quartz.Core/ObjectModel/Attributes/MementoAttribute.cs b/Quartz.Core/ObjectModel/Attributes/MementoAttribute.cs new file mode 100644 index 0000000..fa6e263 --- /dev/null +++ b/Quartz.Core/ObjectModel/Attributes/MementoAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Quartz.Core.ObjectModel.Attributes +{ + /// + /// Signifies that a property, when changed, marks the object as unsaved. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public class MementoAttribute : Attribute { } +} \ No newline at end of file diff --git a/Quartz.Core/ObjectModel/Element.cs b/Quartz.Core/ObjectModel/Element.cs new file mode 100644 index 0000000..e92fecd --- /dev/null +++ b/Quartz.Core/ObjectModel/Element.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using PersistentEntity; +using Quartz.Core.ObjectModel.Attributes; + +namespace Quartz.Core.ObjectModel +{ + /// + /// Represents an elemental type. + /// + public class Element : SaveableDatabaseObject + { + /// + /// The list of damage matchups defined for this element. + /// + [Memento] + public List? ElementMatchups { get; set; } + + /// + /// The name of the element. + /// + [Memento] + public string? Name { get; set; } + } +} \ No newline at end of file diff --git a/Quartz.Core/ObjectModel/ElementMatchup.cs b/Quartz.Core/ObjectModel/ElementMatchup.cs new file mode 100644 index 0000000..4fa2ba9 --- /dev/null +++ b/Quartz.Core/ObjectModel/ElementMatchup.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; +using PersistentEntity; +using Quartz.Core.ObjectModel.Attributes; +using ReactiveUI.Fody.Helpers; + +namespace Quartz.Core.ObjectModel +{ + /// + /// Represents the efficacy of one element against another. + /// + public class ElementMatchup : SaveableDatabaseObject + { + /// + /// The attacking element. + /// + [Memento] + [Reactive] + public Element AttackingElement { get; set; } + + /// + /// The ID of the attacking element. + /// + [Memento] + [Reactive] + public int AttackingElementID { get; set; } + + /// + /// The defending element. + /// + [Memento] + [Reactive] + public Element DefendingElement { get; set; } + + /// + /// The ID of the defending element. + /// + [Memento] + [Reactive] + public int DefendingElementID { get; set; } + + /// + /// Represents the damage multiplier for the matchup. 0.0 means immune, and 1.0 is normal damage. + /// + [Memento] + [Reactive] + public double Multiplier { get; set; } + } +} \ No newline at end of file diff --git a/Quartz.Core/ObjectModel/QuartzContext.cs b/Quartz.Core/ObjectModel/QuartzContext.cs new file mode 100644 index 0000000..b305dda --- /dev/null +++ b/Quartz.Core/ObjectModel/QuartzContext.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.EntityFrameworkCore; +using PersistentEntity; +using ReactiveUI; + +namespace Quartz.Core.ObjectModel +{ + public class QuartzContext : DatabaseContext + { + public DbSet ElementMatchups { get; set; } + + public DbSet Elements { get; set; } + + public QuartzContext(Connection connection) : base(connection) { } + + public QuartzContext() : base() { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasOne(pt => pt.AttackingElement) + .WithMany(p => p.ElementMatchups); + + modelBuilder.Entity() + .HasOne(pt => pt.DefendingElement) + .WithMany() + .OnDelete(DeleteBehavior.Restrict); + } + } +} \ No newline at end of file diff --git a/Quartz.Core/ObjectModel/SaveableDatabaseObject.cs b/Quartz.Core/ObjectModel/SaveableDatabaseObject.cs new file mode 100644 index 0000000..67092aa --- /dev/null +++ b/Quartz.Core/ObjectModel/SaveableDatabaseObject.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading.Tasks; +using PersistentEntity; + +namespace Quartz.Core.ObjectModel +{ + /// + /// An that also tracks relevant saveable changes. + /// + public class SaveableDatabaseObject : SaveableObject, IDatabaseObject + { + /// + /// The unique identifier for the database object. + /// + public int ID { get; set; } + + /// + /// Used for concurrency checks involving simultaneous connections to the database. + /// + [Timestamp] + public byte[]? RowVersion { get; set; } + + /// + /// Saves asynchronously to the database. + /// + /// + /// The database connection to use. + /// + public override async Task SaveAsync(Connection? connection = null) + { + if (connection is { }) + { + using (DatabaseTransaction transaction = new DatabaseTransaction(connection)) + { + await transaction.AddOrUpdateAsync(this); + await transaction.SaveChangesAsync(); + } + this.IsSaved = true; + } + } + } +} \ No newline at end of file diff --git a/Quartz.Core/ObjectModel/SaveableObject.cs b/Quartz.Core/ObjectModel/SaveableObject.cs new file mode 100644 index 0000000..27784d4 --- /dev/null +++ b/Quartz.Core/ObjectModel/SaveableObject.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Reactive.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using DynamicData; +using PersistentEntity; +using Quartz.Core.ObjectModel.Attributes; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; + +namespace Quartz.Core.ObjectModel +{ + /// + /// A that tracks relevant saveable changes. + /// + public abstract class SaveableObject : ReactiveObject + { + /// + /// Gets a list of all s. + /// + public static SourceList SaveableObjects { get; } = new SourceList(); + + /// + /// Gets or sets whether or not this object matches the saved data model on disk. + /// + [Reactive] + [NotMapped] + public bool IsSaved { get; set; } = true; + + /// + /// Creates a new . + /// + public SaveableObject() + { + SaveableObjects.Add(this); + this.PropertyChanged += this.ObservableObjectPropertyChanged; + } + + private void EvaluateSavableChanges(string propertyName) + { + List properties = this.GetType().GetProperties().Where(prop => prop.IsDefined(typeof(MementoAttribute), true)).ToList(); + if (properties.Any(x => x.Name == propertyName)) + { + this.IsSaved = false; + } + } + + private void ObservableObjectPropertyChanged(object sender, PropertyChangedEventArgs args) => this.EvaluateSavableChanges(args.PropertyName); + + /// + /// Saves all unsaved objects asynchronously, then marks them as saved. + /// + /// + /// + /// + /// + public static async Task SaveAllAsync(Connection connection) + { + IEnumerable unsavedFileObjects = SaveableObjects.Items.Where(x => !x.IsSaved && !(x is IDatabaseObject)); + IEnumerable unsavedDbObjects = SaveableObjects.Items.Where(x => !x.IsSaved && x is IDatabaseObject).Cast(); + foreach (SaveableObject unsavedFileObject in unsavedFileObjects) + { + await unsavedFileObject.SaveAsync(connection); + } + if (unsavedDbObjects.Count() > 0) + { + using (DatabaseTransaction transaction = new DatabaseTransaction(connection)) + { + foreach (IDatabaseObject unsavedDbObject in unsavedDbObjects) + { + await transaction.AddOrUpdateAsync(unsavedDbObject); + } + await transaction.SaveChangesAsync(); + } + foreach (SaveableObject unsavedDbObject in SaveableObjects.Items.Where(x => !x.IsSaved && x is IDatabaseObject)) + { + unsavedDbObject.IsSaved = true; + } + } + } + + /// + /// Removes this from the list of tracked s. + /// + public void Remove() => SaveableObjects.Remove(this); + + /// + /// Saves the object asynchronously, and marks it as saved. + /// + /// + /// An optional connection to pass in. + /// + public abstract Task SaveAsync(Connection? connection = null); + } +} \ No newline at end of file diff --git a/Quartz.Core/Quartz.Core.csproj b/Quartz.Core/Quartz.Core.csproj index bcbd448..775dc62 100644 --- a/Quartz.Core/Quartz.Core.csproj +++ b/Quartz.Core/Quartz.Core.csproj @@ -2,12 +2,52 @@ netcoreapp3.1 - 1.1.5.3 - 1.1.5.3 + 1.2.1.69 + 1.2.1.69 + 8.0 + enable + + + + C:\Users\david\Dropbox\Development\Visual Studio\Quartz\Quartz.Core\Quartz.Core.xml + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + All + + + + + + + + + + + + + diff --git a/Quartz.Core/Quartz.Core.xml b/Quartz.Core/Quartz.Core.xml new file mode 100644 index 0000000..dc40234 --- /dev/null +++ b/Quartz.Core/Quartz.Core.xml @@ -0,0 +1,182 @@ + + + + Quartz.Core + + + + + Logs a message to the sink pool before and/or after a method call. + + + + + The message to log before the method is called. + + + + + The message to log if the method throws an exception. + + + + + The message to log after a method is called. + + + + + Determines whether the thrown exception should be considered a fatal exception. + + + + + Logs a message to the sink pool before and/or after a method call. + + + The message to log before the method is called. + + + The message to log after a method is completed. + + + The message to log in the event that the method throws an exception. + + + Tells the logger that any exception thrown in this method is considered a fatal + exception, and should be logged as such. + + + + + Manages logging setup and functions. + + + + + Sets up the color theme for ESDashboard. + + + + + Initializes the log manager. Logging sinks are set up here. + + + + + Signifies that a property, when changed, marks the object as unsaved. + + + + + Represents an elemental type. + + + + + The list of damage matchups defined for this element. + + + + + The name of the element. + + + + + Represents the efficacy of one element against another. + + + + + The attacking element. + + + + + The ID of the attacking element. + + + + + The defending element. + + + + + The ID of the defending element. + + + + + Represents the damage multiplier for the matchup. 0.0 means immune, and 1.0 is normal damage. + + + + + An that also tracks relevant saveable changes. + + + + + The unique identifier for the database object. + + + + + Used for concurrency checks involving simultaneous connections to the database. + + + + + Saves asynchronously to the database. + + + The database connection to use. + + + + + A that tracks relevant saveable changes. + + + + + Gets a list of all s. + + + + + Gets or sets whether or not this object matches the saved data model on disk. + + + + + Creates a new . + + + + + Saves all unsaved objects asynchronously, then marks them as saved. + + + + + + + + + Removes this from the list of tracked s. + + + + + Saves the object asynchronously, and marks it as saved. + + + An optional connection to pass in. + + + + diff --git a/Quartz.Engine/FodyWeavers.xml b/Quartz.Engine/FodyWeavers.xml new file mode 100644 index 0000000..ac77733 --- /dev/null +++ b/Quartz.Engine/FodyWeavers.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Quartz.Engine/FodyWeavers.xsd b/Quartz.Engine/FodyWeavers.xsd new file mode 100644 index 0000000..a097b30 --- /dev/null +++ b/Quartz.Engine/FodyWeavers.xsd @@ -0,0 +1,76 @@ + + + + + + + + + + + + Used to control if the On_PropertyName_Changed feature is enabled. + + + + + Used to control if the Dependent properties feature is enabled. + + + + + Used to control if the IsChanged property feature is enabled. + + + + + Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. + + + + + Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. + + + + + Used to control if equality checks should use the Equals method resolved from the base class. + + + + + Used to control if equality checks should use the static Equals method resolved from the base class. + + + + + Used to turn off build warnings from this weaver. + + + + + Used to turn off build warnings about mismatched On_PropertyName_Changed methods. + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/Quartz.Engine/GameCore.cs b/Quartz.Engine/GameCore.cs new file mode 100644 index 0000000..457d524 --- /dev/null +++ b/Quartz.Engine/GameCore.cs @@ -0,0 +1,87 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Quartz.Engine +{ + /// + /// This is the main type for your game. + /// + public class GameCore : Game + { + private readonly GraphicsDeviceManager graphics; + + private SpriteBatch? spriteBatch; + + /// + /// Creates a new . + /// + public GameCore() + { + this.graphics = new GraphicsDeviceManager(this); + this.Content.RootDirectory = "Content"; + } + + /// + /// This is called when the game should draw itself. + /// + /// + /// Provides a snapshot of timing values. + /// + protected override void Draw(GameTime gameTime) + { + this.GraphicsDevice.Clear(Color.CornflowerBlue); + + // TODO: Add your drawing code here + + base.Draw(gameTime); + } + + /// + /// Allows the game to perform any initialization it needs to before starting to run. This + /// is where it can query for any required services and load any non-graphic related + /// content. Calling base.Initialize will enumerate through any components and initialize + /// them as well. + /// + protected override void Initialize() => + + // TODO: Add your initialization logic here + + base.Initialize(); + + /// + /// LoadContent will be called once per game and is the place to load all of your content. + /// + protected override void LoadContent() => + + // Create a new SpriteBatch, which can be used to draw textures. + this.spriteBatch = new SpriteBatch(this.GraphicsDevice);// TODO: use this.Content to load your game content here + + /// + /// UnloadContent will be called once per game and is the place to unload game-specific content. + /// + protected override void UnloadContent() + { + // TODO: Unload any non ContentManager content here + } + + /// + /// Allows the game to run logic such as updating the world, checking for collisions, + /// gathering input, and playing audio. + /// + /// + /// Provides a snapshot of timing values. + /// + protected override void Update(GameTime gameTime) + { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) + { + this.Exit(); + } + + // TODO: Add your update logic here + + base.Update(gameTime); + } + } +} \ No newline at end of file diff --git a/Quartz.Engine/Icon.bmp b/Quartz.Engine/Icon.bmp new file mode 100644 index 0000000..9dc7e24 Binary files /dev/null and b/Quartz.Engine/Icon.bmp differ diff --git a/Quartz.Engine/Program.cs b/Quartz.Engine/Program.cs new file mode 100644 index 0000000..9deada0 --- /dev/null +++ b/Quartz.Engine/Program.cs @@ -0,0 +1,22 @@ +using System; + +namespace Quartz.Engine +{ + /// + /// The main class. + /// + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + using (GameCore? game = new GameCore()) + { + game.Run(); + } + } + } +} \ No newline at end of file diff --git a/Quartz.Engine/Quartz.Engine.csproj b/Quartz.Engine/Quartz.Engine.csproj new file mode 100644 index 0000000..9f47a17 --- /dev/null +++ b/Quartz.Engine/Quartz.Engine.csproj @@ -0,0 +1,80 @@ + + + + + WinExe + netcoreapp3.1 + DesktopGL + 1.2.1.37 + 1.2.1.37 + Quartz.ico + 8.0 + enable + + + + 1701;1702;NU1701 + C:\Users\david\Dropbox\Development\Visual Studio\Quartz\Quartz.Engine\Quartz.Engine.xml + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + all + + + + + + + + + + + + + + + + + + + NU1701 + + + \ No newline at end of file diff --git a/Quartz.Engine/Quartz.Engine.xml b/Quartz.Engine/Quartz.Engine.xml new file mode 100644 index 0000000..a49155d --- /dev/null +++ b/Quartz.Engine/Quartz.Engine.xml @@ -0,0 +1,63 @@ + + + + Quartz.Engine + + + + + This is the main type for your game. + + + + + Creates a new . + + + + + This is called when the game should draw itself. + + + Provides a snapshot of timing values. + + + + + Allows the game to perform any initialization it needs to before starting to run. This + is where it can query for any required services and load any non-graphic related + content. Calling base.Initialize will enumerate through any components and initialize + them as well. + + + + + LoadContent will be called once per game and is the place to load all of your content. + + + + + UnloadContent will be called once per game and is the place to unload game-specific content. + + + + + Allows the game to run logic such as updating the world, checking for collisions, + gathering input, and playing audio. + + + Provides a snapshot of timing values. + + + + + The main class. + + + + + The main entry point for the application. + + + + diff --git a/Quartz.Engine/Quartz.ico b/Quartz.Engine/Quartz.ico new file mode 100644 index 0000000..ec8289a Binary files /dev/null and b/Quartz.Engine/Quartz.ico differ diff --git a/Quartz.Engine/app.manifest b/Quartz.Engine/app.manifest new file mode 100644 index 0000000..5f4c221 --- /dev/null +++ b/Quartz.Engine/app.manifest @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + + diff --git a/Quartz.IDE/App.xaml b/Quartz.IDE/App.xaml index 1b696b0..49dbe6b 100644 --- a/Quartz.IDE/App.xaml +++ b/Quartz.IDE/App.xaml @@ -2,8 +2,20 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Quartz.IDE" - StartupUri="MainWindow.xaml"> + xmlns:l="clr-namespace:Librarium.WPF.UI;assembly=Librarium.WPF" + Startup="AppStartup" + ShutdownMode="OnExplicitShutdown"> - + + + + + + + + + + - + \ No newline at end of file diff --git a/Quartz.IDE/App.xaml.cs b/Quartz.IDE/App.xaml.cs index f594f16..ac48b60 100644 --- a/Quartz.IDE/App.xaml.cs +++ b/Quartz.IDE/App.xaml.cs @@ -2,16 +2,127 @@ using System.Collections.Generic; using System.Configuration; using System.Data; +using System.IO; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using System.Windows; +using System.Windows.Data; +using DynamicData; +using Librarium.Core; +using Librarium.Json; +using Microsoft.Build.Locator; +using Quartz.Core.Diagnostics; +using Quartz.IDE.Converters; +using Quartz.IDE.Json; +using Quartz.IDE.ObjectModel; +using Quartz.IDE.Windows; +using ReactiveUI; +using Serilog; +using Splat; namespace Quartz.IDE { /// - /// Interaction logic for App.xaml + /// Houses global application data and the entry point of the application. /// public partial class App : Application { + /// + /// Gets the metadata for the application. + /// + public static Meta Metadata { get; } = new Meta(); + + /// + /// Gets the currently loaded preferences for the user. + /// + public static Preferences Preferences { get; set; } = null!; + + /// + /// When the application ends from anywhere, the log needs to be closed and flushed. + /// + [Log("Quartz will now flush the logger and shut down.")] + private static void OnExit(object? sender, EventArgs args) => Log.CloseAndFlush(); + + /// + /// Serves as the application entry point. + /// + /// + /// The source of the event. + /// + /// + /// Contains the arguments pertaining to the application startup event. + /// + private void AppStartup(object? sender, StartupEventArgs args) + { + DefineDebugFlag(); + + InitializeLogging(); + + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnExit); + + EnterProgram(); + } + + /// + /// Enters the program. + /// + [Log("Starting...", "Quartz is now running.", "An unhandled exception has occurred.")] + private static void EnterProgram() + { + UpdateManager.Initialize(); + + MSBuildLocator.RegisterDefaults(); + + InitializePreferences(); + + Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); + + Locator.CurrentMutable.RegisterConstant(new TextToFlowDocumentConverter(), + typeof(IBindingTypeConverter)); + + new MainWindow().Show(); + } + + /// + /// Loads the Preferences from disk or creates default preferences if none exists. + /// + [Log("Initializing Preferences...", "Preferences initialization complete.")] + private static void InitializePreferences() + { + if (!Directory.Exists(Metadata.AppDataDirectory)) + { + Directory.CreateDirectory(Metadata.AppDataDirectory); + } + + Preferences = JFile.Load(Metadata.AppDataDirectory, "preferences.json").CreateModel(); + Preferences.Save(); + Preferences.Load(); + } + + /// + /// Initializes the logging manager and logs the welcome messages. + /// + private static void InitializeLogging() + { + LogManager.Initialize(); + + Log.Information("Welcome to Quartz IDE Version {Version}.", Metadata.Version); + + if (Metadata.IsDebug) + { + Log.Warning("This assembly is running in DEBUG mode."); + } + } + + /// + /// Defines the flag that determines whether or not the program is running in debug mode. + /// + private static void DefineDebugFlag() + { +#if DEBUG + Metadata.IsDebug = true; +#endif + } } -} +} \ No newline at end of file diff --git a/Quartz.IDE/Commands/NewProjectCommand.cs b/Quartz.IDE/Commands/NewProjectCommand.cs new file mode 100644 index 0000000..98ff566 --- /dev/null +++ b/Quartz.IDE/Commands/NewProjectCommand.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Input; +using Librarium.Commands; +using Quartz.IDE.Windows; + +namespace Quartz.IDE.Commands +{ + /// + /// Creates a new project. + /// + public class NewProjectCommand : RelayCommand + { + public static ICommand Instance { get; } = new NewProjectCommand(); + + public override bool CanExecute(object parameter) => true; + + public override void Execute(object parameter) + { + if (App.Metadata.CurrentProject is null) + { + NewProjectWindow wnd = new NewProjectWindow(); + wnd.ShowDialog(); + } + else + { + MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show($"Changes have been made to {App.Metadata.CurrentProject.Name}. Would you like to save these changes before closing the project?", "Save Changes?", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) + { + App.Metadata.CurrentProject.Save(); + NewProjectWindow wnd = new NewProjectWindow(); + wnd.ShowDialog(); + } + else if (result == MessageBoxResult.No) + { + NewProjectWindow wnd = new NewProjectWindow(); + wnd.ShowDialog(); + } + else + { + return; + } + } + } + } +} \ No newline at end of file diff --git a/Quartz.IDE/Commands/OpenAboutWindowCommand.cs b/Quartz.IDE/Commands/OpenAboutWindowCommand.cs new file mode 100644 index 0000000..9076f45 --- /dev/null +++ b/Quartz.IDE/Commands/OpenAboutWindowCommand.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Input; +using Librarium.Commands; +using Quartz.IDE.Windows; + +namespace Quartz.IDE.Commands +{ + public class OpenAboutWindowCommand : RelayCommand + { + public static ICommand Instance { get; } = new OpenAboutWindowCommand(); + + public override bool CanExecute(object parameter) => true; + + public override void Execute(object parameter) + { + AboutWindow wnd = new AboutWindow(); + wnd.ShowDialog(); + } + } +} \ No newline at end of file diff --git a/Quartz.IDE/Commands/OpenProjectCommand.cs b/Quartz.IDE/Commands/OpenProjectCommand.cs new file mode 100644 index 0000000..a0e5df5 --- /dev/null +++ b/Quartz.IDE/Commands/OpenProjectCommand.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Windows; +using System.Windows.Input; +using DynamicData; +using Librarium.Commands; +using Librarium.Core; +using Librarium.Json; +using Microsoft.WindowsAPICodePack.Dialogs; +using Quartz.IDE.Json; + +namespace Quartz.IDE.Commands +{ + /// + /// Opens a project. + /// + public class OpenProjectCommand : RelayCommand + { + public static ICommand Instance { get; } = new OpenProjectCommand(); + + private string? CommonFileDialog() + { + CommonOpenFileDialog dlg = new CommonOpenFileDialog + { + Title = "Select a project file to open...", + IsFolderPicker = false, + InitialDirectory = @"C:\", + AddToMostRecentlyUsedList = false, + AllowNonFileSystemItems = false, + DefaultDirectory = @"C:\", + EnsureFileExists = true, + EnsurePathExists = true, + EnsureReadOnly = false, + EnsureValidNames = true, + Multiselect = false, + ShowPlacesList = true + }; + + dlg.Filters.Add(new CommonFileDialogFilter("JSON Project Files", "*.json")); + + return dlg.ShowDialog() == CommonFileDialogResult.Ok ? dlg.FileName : null; + } + + private int OpenFromDialog() + { + string? path = this.CommonFileDialog(); + return path.IsNullOrWhiteSpace() ? 1 : this.OpenFromPath(path!); + } + + private int OpenFromPath(string path) + { + bool? saveBeforeClosing = true; + if (App.Metadata.CurrentProject is { } && !App.Metadata.CurrentProject.IsSaved) + { + MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show( + $"Changes have been made to {App.Metadata.CurrentProject.Name}. Would you like to save these changes before closing the project?", + "Save Changes?", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning); + + saveBeforeClosing = result switch + { + MessageBoxResult.Yes => true, + MessageBoxResult.No => false, + _ => null, + }; + if (saveBeforeClosing is null) { return 1; } + } + + return this.PrepareProject(path, saveBeforeClosing); + } + + private int PrepareProject(string path, bool? saveBeforeClosing = true) + { + if (path is null) { return 1; } + ProjectFile file = JFile.Load(path, "Project.json"); + if (file is null) { return 1; } + if (file.Version is null) + { + Xceed.Wpf.Toolkit.MessageBox.Show($"This project file was made using 1.1, and is not compatible with the current release ({AppMeta.CurrentVersion}). Your project will not be harmed, but if you want to open it, you will have to revert back to 1.1.", "Incompatible Project", MessageBoxButton.OK); + return 1; + } + if (App.Metadata.CurrentProject is { }) { App.Metadata.CurrentProject.Close(saveBeforeClosing ?? true).Wait(); } + App.Metadata.CurrentProject = file.CreateModel(); + App.Preferences.RecentlyOpenedProjects.AddOrUpdate( + new RecentItem( + App.Metadata.CurrentProject.Name ?? "", + App.Metadata.CurrentProject.FilePath, + DateTime.Now)); + App.Metadata.CurrentProject.Load(); + App.Preferences.Save(); + return 0; + } + + public override bool CanExecute(object parameter) => true; + + public override void Execute(object parameter) + { + int success = 0; + if (parameter is string path) + { + success = this.OpenFromPath(path); + } + else if (parameter is null) + { + success = this.OpenFromDialog(); + } + if (success == 0) + { + App.Metadata.CurrentProject!.IsSaved = true; + } + } + } +} \ No newline at end of file diff --git a/Quartz.IDE/Controls/BindableDataGridComboBoxColumn.cs b/Quartz.IDE/Controls/BindableDataGridComboBoxColumn.cs new file mode 100644 index 0000000..fab6389 --- /dev/null +++ b/Quartz.IDE/Controls/BindableDataGridComboBoxColumn.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; + +namespace Quartz.IDE.Controls +{ + /// + /// Represents a bindable that hosts a in + /// its cells. + /// + public class BindableDataGridComboBoxColumn : DataGridComboBoxColumn + { + private void CopyItemsSource(FrameworkElement element) => BindingOperations.SetBinding(element, ComboBox.ItemsSourceProperty, + BindingOperations.GetBinding(this, ComboBox.ItemsSourceProperty)); + + /// + /// Gets a combo box control that is bound to the column's , , values. + /// + /// + /// The cell that will contain the generated element. + /// + /// + /// The data item represented by the row that contains + /// + /// + /// + + protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) + { + FrameworkElement element = base.GenerateEditingElement(cell, dataItem); + this.CopyItemsSource(element); + return element; + } + + /// + /// Gets a read-only combo box control that is bound to the column's , , values. + /// + /// + /// The cell that will contain the generated element. + /// + /// + /// The data item represented by the row that contains the intended cell. + /// + + protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) + { + FrameworkElement element = base.GenerateElement(cell, dataItem); + this.CopyItemsSource(element); + return element; + } + } +} \ No newline at end of file diff --git a/Quartz.IDE/Controls/ElementsPage.xaml b/Quartz.IDE/Controls/ElementsPage.xaml new file mode 100644 index 0000000..bd266fb --- /dev/null +++ b/Quartz.IDE/Controls/ElementsPage.xaml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + +