Refactor activation handling and introduce entity system
Added new `ActivationHandler` class for folder initialization. Added `ProjectService` class for project-related operations. Added `Ghost.Entities` project with entity management classes. Added `EngineEditorWindow` for enhanced user interface. Changed project files to restructure dependencies and remove unused references. Changed `ProjectRepository` to use asynchronous methods for improved performance. Changed data binding in `CreateProjectPage.xaml` and `OpenProjectPage.xaml` to use new data models. Changed `App.xaml.cs` to utilize the new `ActivationHandler` and include additional services. Removed `IActivationHandler` interface and integrated its functionality into `ActivationHandler`. Removed `EditorActivationHandler` as its functionality was merged into `ActivationHandler`. Updated `AssemblyInfo.cs` to include global using directives for entity types. Updated image assets to reflect visual resource changes.
@@ -1,13 +0,0 @@
|
|||||||
namespace Ghost.Core.Activation;
|
|
||||||
|
|
||||||
public abstract class ActivationHandler<T> : IActivationHandler
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
protected virtual bool CanHandleInternal(T args) => true;
|
|
||||||
|
|
||||||
protected abstract Task HandleInternalAsync(T args);
|
|
||||||
|
|
||||||
public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!);
|
|
||||||
|
|
||||||
public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Ghost.Core.Activation;
|
|
||||||
|
|
||||||
public interface IActivationHandler
|
|
||||||
{
|
|
||||||
bool CanHandle(object args);
|
|
||||||
|
|
||||||
Task HandleAsync(object args);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,56 +1,56 @@
|
|||||||
using Ghost.Database.Models.Projects;
|
using Ghost.Data.Models;
|
||||||
using Ghost.Engine.Constants;
|
using Ghost.Data.Resources;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
|
|
||||||
namespace Ghost.Database.DataContext;
|
namespace Ghost.Data.DataContext;
|
||||||
|
|
||||||
public class ProjectRepository
|
internal static class ProjectRepository
|
||||||
{
|
{
|
||||||
private const string _CONNECTION_STRING = "Data Source={0}\\projects.db;Version=3;";
|
private const string _CONNECTION_STRING = "Data Source={0}\\projects.db;Version=3;";
|
||||||
private const string _CREATE_PROJECT_TABLE_STRING = "CREATE TABLE IF NOT EXISTS Projects (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Path TEXT, EngineVersion TEXT, LastOpened DATETIME);";
|
private const string _CREATE_PROJECT_TABLE_STRING = "CREATE TABLE IF NOT EXISTS Projects (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Path TEXT, EngineVersion TEXT, LastOpened DATETIME);";
|
||||||
private const string _SELECT_PROJECT_STRING = "SELECT * FROM Projects";
|
private const string _SELECT_PROJECT_STRING = "SELECT * FROM Projects";
|
||||||
private const string _INSERT_PROJECT_STRING = "INSERT INTO Projects (Name, Path, EngineVersion, LastOpened) VALUES (@Name, @Path, @EngineVersion, @LastOpened);";
|
private const string _INSERT_PROJECT_STRING = "INSERT INTO Projects (Name, Path, EngineVersion, LastOpened) VALUES (@Name, @Path, @EngineVersion, @LastOpened);";
|
||||||
|
|
||||||
private static string GetConnectionString() => string.Format(_CONNECTION_STRING, EngineDataPath.ApplicationDataFolder);
|
private static string GetConnectionString() => string.Format(_CONNECTION_STRING, DataPath.ApplicationDataFolder);
|
||||||
|
|
||||||
private static void EnsureTableCreated(SQLiteConnection connection)
|
private static async Task EnsureTableCreatedAsync(SQLiteConnection connection)
|
||||||
{
|
{
|
||||||
using var createCommand = connection.CreateCommand();
|
using var createCommand = connection.CreateCommand();
|
||||||
createCommand.CommandText = _CREATE_PROJECT_TABLE_STRING;
|
createCommand.CommandText = _CREATE_PROJECT_TABLE_STRING;
|
||||||
createCommand.ExecuteNonQuery();
|
await createCommand.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<ProjectInfo> LoadProjects()
|
public static async IAsyncEnumerable<ProjectInfo> LoadProjectsAsync()
|
||||||
{
|
{
|
||||||
using var connection = new SQLiteConnection(GetConnectionString());
|
using var connection = new SQLiteConnection(GetConnectionString());
|
||||||
connection.Open();
|
await connection.OpenAsync();
|
||||||
|
|
||||||
EnsureTableCreated(connection);
|
await EnsureTableCreatedAsync(connection);
|
||||||
|
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = _SELECT_PROJECT_STRING;
|
command.CommandText = _SELECT_PROJECT_STRING;
|
||||||
|
|
||||||
using var reader = command.ExecuteReader();
|
using var reader = command.ExecuteReader();
|
||||||
while (reader.Read())
|
while (await reader.ReadAsync())
|
||||||
{
|
{
|
||||||
var project = new ProjectInfo
|
var project = new ProjectInfo
|
||||||
{
|
{
|
||||||
Name = reader.GetString(0),
|
Name = reader.GetString(1),
|
||||||
Path = reader.GetString(1),
|
Path = reader.GetString(2),
|
||||||
EngineVersion = new Version(reader.GetString(2)),
|
EngineVersion = new Version(reader.GetString(3)),
|
||||||
LastOpened = reader.GetDateTime(3)
|
LastOpened = reader.GetDateTime(4)
|
||||||
};
|
};
|
||||||
|
|
||||||
yield return project;
|
yield return project;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddProject(ProjectInfo project)
|
public static async Task AddProjectAsync(ProjectInfo project)
|
||||||
{
|
{
|
||||||
using var connection = new SQLiteConnection(GetConnectionString());
|
using var connection = new SQLiteConnection(GetConnectionString());
|
||||||
connection.Open();
|
await connection.OpenAsync();
|
||||||
|
|
||||||
EnsureTableCreated(connection);
|
await EnsureTableCreatedAsync(connection);
|
||||||
|
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = _INSERT_PROJECT_STRING;
|
command.CommandText = _INSERT_PROJECT_STRING;
|
||||||
@@ -59,6 +59,6 @@ public class ProjectRepository
|
|||||||
command.Parameters.AddWithValue("@Path", project.Path);
|
command.Parameters.AddWithValue("@Path", project.Path);
|
||||||
command.Parameters.AddWithValue("@EngineVersion", project.EngineVersion.ToString());
|
command.Parameters.AddWithValue("@EngineVersion", project.EngineVersion.ToString());
|
||||||
command.Parameters.AddWithValue("@LastOpened", project.LastOpened);
|
command.Parameters.AddWithValue("@LastOpened", project.LastOpened);
|
||||||
command.ExecuteNonQuery();
|
await command.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,18 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
|
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
|
||||||
<PackageReference Include="System.Data.SQLite" Version="1.0.119" />
|
<PackageReference Include="System.Data.SQLite" Version="1.0.119" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="4.7.3" />
|
<PackageReference Include="System.Drawing.Common" Version="4.7.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
10
Ghost.Data/JsonContext.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Ghost.Data.Models;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ghost.Data;
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(TemplateInfo))]
|
||||||
|
internal partial class JsonContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Ghost.Database.Models.Projects;
|
namespace Ghost.Data.Models;
|
||||||
|
|
||||||
public class ProjectInfo
|
public class ProjectInfo
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
using Ghost.Database.Models.Projects;
|
namespace Ghost.Data.Models;
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Models.Warpers;
|
public class TemplateInfo
|
||||||
|
{
|
||||||
|
public required string Name
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
internal class TemplateInfoWarper(string templatePath, TemplateInfo info)
|
public string? Description
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public required Version TemplateVersion
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public required Version EngineVersion
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TemplateData(string templatePath, TemplateInfo info)
|
||||||
{
|
{
|
||||||
private const string _ICON_NAME = "icon.png";
|
private const string _ICON_NAME = "icon.png";
|
||||||
private const string _PREVIEW_NAME = "preview.png";
|
private const string _PREVIEW_NAME = "preview.png";
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Ghost.Engine.Constants;
|
namespace Ghost.Data.Resources;
|
||||||
|
|
||||||
internal static class EngineDataPath
|
public class DataPath
|
||||||
{
|
{
|
||||||
public const string ENGINE_DATA_FOLDER_NAME = "GhostEngine";
|
public const string ENGINE_DATA_FOLDER_NAME = "GhostEngine";
|
||||||
|
|
||||||
public static string ApplicationDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ENGINE_DATA_FOLDER_NAME);
|
public static string ApplicationDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ENGINE_DATA_FOLDER_NAME);
|
||||||
|
public static string ProjectTemplatesFolder = Path.Combine(ApplicationDataFolder, "ProjectTemplates");
|
||||||
}
|
}
|
||||||
88
Ghost.Data/Services/ProjectService.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using Ghost.Data.DataContext;
|
||||||
|
using Ghost.Data.Models;
|
||||||
|
using Ghost.Data.Resources;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Ghost.Data.Services;
|
||||||
|
|
||||||
|
public class ProjectService
|
||||||
|
{
|
||||||
|
private const string _TEMPLATE_CONTENT_FILE = "content.zip";
|
||||||
|
|
||||||
|
private const string _ASSETS_FOLDER = "Assets";
|
||||||
|
|
||||||
|
public async IAsyncEnumerable<(string path, TemplateInfo info)> GetProjectTemplatesAsync()
|
||||||
|
{
|
||||||
|
var templatesFolder = DataPath.ProjectTemplatesFolder;
|
||||||
|
if (!Directory.Exists(templatesFolder))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var templates = Directory.GetFiles(DataPath.ProjectTemplatesFolder, "template.json", SearchOption.AllDirectories);
|
||||||
|
foreach (var templatePath in templates)
|
||||||
|
{
|
||||||
|
var fileStream = File.OpenRead(templatePath);
|
||||||
|
var templateInfo = await JsonSerializer.DeserializeAsync<TemplateInfo>(fileStream, JsonContext.Default.TemplateInfo);
|
||||||
|
if (templateInfo == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return (templatePath, templateInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task SetupAssetsFolder(string projectPath, string templatePath)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
var templateContentPath = Path.Combine(templatePath, _TEMPLATE_CONTENT_FILE);
|
||||||
|
var projectAssetsPath = Path.Combine(projectPath, _ASSETS_FOLDER);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(projectAssetsPath);
|
||||||
|
|
||||||
|
if (!File.Exists(templateContentPath))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile.ExtractToDirectory(templateContentPath, projectAssetsPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> CreateProjectAsync(string projectName, string projectDirectory, string templatePath)
|
||||||
|
{
|
||||||
|
var projectPath = Path.Combine(projectDirectory, projectName);
|
||||||
|
if (!Directory.Exists(projectPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetupAssetsFolder(projectPath, templatePath);
|
||||||
|
|
||||||
|
return projectPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AddProjectAsync(ProjectInfo project)
|
||||||
|
{
|
||||||
|
return ProjectRepository.AddProjectAsync(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AddProjectAsync(string name, string path, Version version)
|
||||||
|
{
|
||||||
|
return ProjectRepository.AddProjectAsync(new ProjectInfo
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Path = path,
|
||||||
|
EngineVersion = version,
|
||||||
|
LastOpened = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncEnumerable<ProjectInfo> LoadProjectAsync()
|
||||||
|
{
|
||||||
|
return ProjectRepository.LoadProjectsAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
namespace Ghost.Database.Models.Projects;
|
|
||||||
|
|
||||||
public class TemplateInfo
|
|
||||||
{
|
|
||||||
public required string Name
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? Description
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public required Version TemplateVersion
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public required Version EngineVersion
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, Version>? Packages
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
26
Ghost.Editor/ActivationHandler.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Ghost.Data.Resources;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ghost.Editor;
|
||||||
|
|
||||||
|
internal static class ActivationHandler
|
||||||
|
{
|
||||||
|
private static void FolderInitialization()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(DataPath.ApplicationDataFolder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(DataPath.ApplicationDataFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Directory.Exists(DataPath.ProjectTemplatesFolder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(DataPath.ProjectTemplatesFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Handle(LaunchActivatedEventArgs args)
|
||||||
|
{
|
||||||
|
FolderInitialization();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Ghost.Editor.View.Pages;
|
using Ghost.Data.Services;
|
||||||
|
using Ghost.Editor.View.Pages;
|
||||||
using Ghost.Editor.View.Windows;
|
using Ghost.Editor.View.Windows;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using System;
|
using System;
|
||||||
@@ -39,6 +41,8 @@ namespace Ghost.Editor
|
|||||||
UseContentRoot(AppContext.BaseDirectory).
|
UseContentRoot(AppContext.BaseDirectory).
|
||||||
ConfigureServices((context, services) =>
|
ConfigureServices((context, services) =>
|
||||||
{
|
{
|
||||||
|
services.AddTransient<ProjectService>();
|
||||||
|
|
||||||
HostHelper.SetupPageService(context, services);
|
HostHelper.SetupPageService(context, services);
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
@@ -67,7 +71,7 @@ namespace Ghost.Editor
|
|||||||
{
|
{
|
||||||
base.OnLaunched(args);
|
base.OnLaunched(args);
|
||||||
|
|
||||||
EditorActivationHandler.Handle(args);
|
ActivationHandler.Handle(args);
|
||||||
|
|
||||||
Host.Start();
|
Host.Start();
|
||||||
|
|
||||||
|
|||||||
BIN
Ghost.Editor/Assets/Icon.targetsize-16_altform-lightunplated.png
Normal file
|
After Width: | Height: | Size: 599 B |
BIN
Ghost.Editor/Assets/Icon.targetsize-16_altform-unplated.png
Normal file
|
After Width: | Height: | Size: 580 B |
BIN
Ghost.Editor/Assets/Icon.targetsize-24_altform-lightunplated.png
Normal file
|
After Width: | Height: | Size: 831 B |
BIN
Ghost.Editor/Assets/Icon.targetsize-24_altform-unplated.png
Normal file
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 5.9 KiB |
BIN
Ghost.Editor/Assets/Icon.targetsize-256_altform-unplated.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
Ghost.Editor/Assets/Icon.targetsize-32_altform-lightunplated.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Ghost.Editor/Assets/Icon.targetsize-32_altform-unplated.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Ghost.Editor/Assets/Icon.targetsize-48_altform-lightunplated.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.Editor/Assets/Icon.targetsize-48_altform-unplated.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,9 +0,0 @@
|
|||||||
using Ghost.Engine.Constants;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Constants;
|
|
||||||
|
|
||||||
public static class EditorDataPath
|
|
||||||
{
|
|
||||||
public static string ProjectTemplatesFolder = Path.Combine(EngineDataPath.ApplicationDataFolder, "ProjectsTemplates");
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using Ghost.Editor.Constants;
|
|
||||||
using Ghost.Engine.Constants;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ghost.Editor;
|
|
||||||
|
|
||||||
internal static class EditorActivationHandler
|
|
||||||
{
|
|
||||||
private static void FolderInitialization()
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(EngineDataPath.ApplicationDataFolder))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(EngineDataPath.ApplicationDataFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Directory.Exists(EditorDataPath.ProjectTemplatesFolder))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(EditorDataPath.ProjectTemplatesFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Handle(LaunchActivatedEventArgs args)
|
|
||||||
{
|
|
||||||
FolderInitialization();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,22 +23,28 @@
|
|||||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-256.png" />
|
<Content Remove="Assets\Icon.altform-unplated_targetsize-256.png" />
|
||||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-32.png" />
|
<Content Remove="Assets\Icon.altform-unplated_targetsize-32.png" />
|
||||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-48.png" />
|
<Content Remove="Assets\Icon.altform-unplated_targetsize-48.png" />
|
||||||
<Content Remove="Assets\Icon.scale-100.png" />
|
|
||||||
<Content Remove="Assets\Icon.scale-125.png" />
|
|
||||||
<Content Remove="Assets\Icon.scale-150.png" />
|
|
||||||
<Content Remove="Assets\Icon.scale-200.png" />
|
|
||||||
<Content Remove="Assets\Icon.scale-400.png" />
|
|
||||||
<Content Remove="Assets\Icon.targetsize-16.png" />
|
|
||||||
<Content Remove="Assets\Icon.targetsize-24.png" />
|
|
||||||
<Content Remove="Assets\Icon.targetsize-256.png" />
|
|
||||||
<Content Remove="Assets\Icon.targetsize-32.png" />
|
|
||||||
<Content Remove="Assets\Icon.targetsize-48.png" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Remove="Assets\Icon.scale-100.png" />
|
||||||
|
<None Remove="Assets\Icon.scale-125.png" />
|
||||||
|
<None Remove="Assets\Icon.scale-150.png" />
|
||||||
|
<None Remove="Assets\Icon.scale-200.png" />
|
||||||
|
<None Remove="Assets\Icon.scale-400.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-16.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-16_altform-unplated.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-24.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-24_altform-lightunplated.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-256.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-256_altform-unplated.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-32.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-32_altform-lightunplated.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-48.png" />
|
||||||
|
<None Remove="Assets\Icon.targetsize-48_altform-unplated.png" />
|
||||||
<None Remove="Controls\BasicInput\PropertyField.xaml" />
|
<None Remove="Controls\BasicInput\PropertyField.xaml" />
|
||||||
<None Remove="Controls\EditorControls.xaml" />
|
<None Remove="Controls\EditorControls.xaml" />
|
||||||
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
|
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
|
||||||
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
|
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
|
||||||
|
<None Remove="View\Windows\EngineEditorWindow.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -64,12 +70,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
|
||||||
<PackageReference Include="WinUIEx" Version="2.5.1" />
|
<PackageReference Include="WinUIEx" Version="2.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ghost.Database\Ghost.Database.csproj" />
|
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
|
||||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -88,9 +95,16 @@
|
|||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
<Folder Include="Resources\" />
|
||||||
<Folder Include="Services\" />
|
<Folder Include="Services\" />
|
||||||
<Folder Include="ViewModel\Windows\" />
|
<Folder Include="ViewModel\Windows\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="Controls\BasicInput\PropertyField.xaml">
|
<Page Update="Controls\BasicInput\PropertyField.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@@ -120,5 +134,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<PublishAot>True</PublishAot>
|
||||||
|
<PublishTrimmed>True</PublishTrimmed>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
<uap:VisualElements
|
<uap:VisualElements
|
||||||
DisplayName="GhostEngine"
|
DisplayName="GhostEngine"
|
||||||
Description="GhostEngine"
|
Description="GhostEngine"
|
||||||
BackgroundColor="transparent"
|
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png" BackgroundColor="transparent">
|
||||||
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png">
|
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" >
|
||||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
</uap:DefaultTile >
|
||||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
</uap:VisualElements>
|
</uap:VisualElements>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Ghost.Editor.View.Pages.Landing;
|
using Ghost.Editor.View.Pages.Landing;
|
||||||
using Ghost.Editor.View.Windows;
|
using Ghost.Editor.View.Windows;
|
||||||
using Ghost.Editor.ViewModel.Pages.Landing;
|
using Ghost.Editor.ViewModel.Pages.Landing;
|
||||||
using Ghost.Editor.ViewModel.Windows;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
@@ -12,11 +11,12 @@ internal static partial class HostHelper
|
|||||||
public static void SetupPageService(HostBuilderContext context, IServiceCollection services)
|
public static void SetupPageService(HostBuilderContext context, IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<LandingWindow>();
|
services.AddTransient<LandingWindow>();
|
||||||
services.AddTransient<LandingViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<CreateProjectPage>();
|
services.AddTransient<CreateProjectPage>();
|
||||||
services.AddTransient<CreateProjectViewModel>();
|
services.AddTransient<CreateProjectViewModel>();
|
||||||
|
|
||||||
services.AddTransient<OpenProjectPage>();
|
services.AddTransient<OpenProjectPage>();
|
||||||
|
|
||||||
|
services.AddSingleton<EngineEditorWindow>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:data="using:Ghost.Data.Models"
|
||||||
xmlns:editor="using:Ghost.Editor.Controls"
|
xmlns:editor="using:Ghost.Editor.Controls"
|
||||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:warper="using:Ghost.Editor.Models.Warpers"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
ItemsSource="{x:Bind ViewModel.templates}"
|
ItemsSource="{x:Bind ViewModel.templates}"
|
||||||
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
|
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="warper:TemplateInfoWarper">
|
<DataTemplate x:DataType="data:TemplateData">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
Width="24"
|
Width="24"
|
||||||
Height="24">
|
Height="24">
|
||||||
<ImageIcon.Source>
|
<ImageIcon.Source>
|
||||||
<BitmapImage UriSource="{x:Bind GetIconURI()}" />
|
<BitmapImage UriSource="{x:Bind GetIconURI(), Mode=OneWay}" />
|
||||||
</ImageIcon.Source>
|
</ImageIcon.Source>
|
||||||
</ImageIcon>
|
</ImageIcon>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
Margin="16,0,0,0"
|
Margin="16,0,0,0"
|
||||||
Padding="16"
|
Padding="16"
|
||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
CornerRadius="8">
|
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="300" />
|
<RowDefinition Height="300" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@@ -76,13 +76,19 @@
|
|||||||
<Grid Grid.Row="0" CornerRadius="4">
|
<Grid Grid.Row="0" CornerRadius="4">
|
||||||
<Image VerticalAlignment="Center" Stretch="UniformToFill">
|
<Image VerticalAlignment="Center" Stretch="UniformToFill">
|
||||||
<Image.Source>
|
<Image.Source>
|
||||||
<BitmapImage UriSource="{x:Bind ViewModel.SelectedTemplate.GetPreviewURI()}" />
|
<BitmapImage UriSource="{x:Bind ViewModel.SelectedTemplate.GetPreviewURI(), Mode=OneWay}" />
|
||||||
</Image.Source>
|
</Image.Source>
|
||||||
</Image>
|
</Image>
|
||||||
<Grid
|
<Grid
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Background="{ThemeResource CircleElevationBorderBrush}" />
|
Background="{ThemeResource CircleElevationBorderBrush}">
|
||||||
|
<TextBlock
|
||||||
|
Margin="16"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Foreground="{ThemeResource TextOnAccentFillColorSecondaryBrush}"
|
||||||
|
Text="{x:Bind ViewModel.SelectedTemplate.Info.Description, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Grid.Row="1" Margin="8,0">
|
<StackPanel Grid.Row="1" Margin="8,0">
|
||||||
@@ -106,7 +112,10 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<TextBox Grid.Column="0" Text="{x:Bind ViewModel.ProjectLocation, Mode=TwoWay}" />
|
<TextBox
|
||||||
|
Grid.Column="0"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Text="{x:Bind ViewModel.ProjectLocation, Mode=TwoWay}" />
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="4,0,0,0"
|
Margin="4,0,0,0"
|
||||||
@@ -122,6 +131,7 @@
|
|||||||
<Button
|
<Button
|
||||||
Width="150"
|
Width="150"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
|
Command="{x:Bind ViewModel.CreateProjectCommand}"
|
||||||
Content="Create"
|
Content="Create"
|
||||||
Style="{ThemeResource AccentButtonStyle}" />
|
Style="{ThemeResource AccentButtonStyle}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -4,25 +4,57 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:data="using:Ghost.Database.Models.Projects"
|
xmlns:data="using:Ghost.Data.Models"
|
||||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0" Margin="16,8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="200" />
|
||||||
|
<ColumnDefinition Width="200" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="Name" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="Last Open" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="2"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="Engine Version" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
x:Name="ProjectListView"
|
x:Name="ProjectListView"
|
||||||
|
Grid.Row="1"
|
||||||
|
Padding="16"
|
||||||
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
|
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||||
IsItemClickEnabled="True"
|
IsItemClickEnabled="True"
|
||||||
ItemClick="ListView_ItemClick"
|
ItemClick="ListView_ItemClick"
|
||||||
ItemsSource="{x:Bind projects}"
|
ItemsSource="{x:Bind projects}"
|
||||||
Visibility="Visible">
|
Visibility="Visible">
|
||||||
<ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="data:ProjectInfo">
|
<DataTemplate x:DataType="data:ProjectInfo">
|
||||||
<Grid>
|
<Grid Padding="4,8">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="200" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="100" />
|
||||||
|
<ColumnDefinition Width="100" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
@@ -37,12 +69,28 @@
|
|||||||
Text="{x:Bind Name}" />
|
Text="{x:Bind Name}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind Path}" />
|
Text="{x:Bind Path}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Grid.Column="1" Text="{x:Bind LastOpened}" />
|
<TextBlock
|
||||||
<TextBlock Grid.Column="2" Text="{x:Bind EngineVersion}" />
|
Grid.Column="1"
|
||||||
|
Margin="16,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Bind LastOpened}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="16,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Bind EngineVersion}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="3"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0">
|
||||||
|
<FontIcon Glyph="" />
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
using Ghost.Database.DataContext;
|
using Ghost.Data.Models;
|
||||||
using Ghost.Database.Models.Projects;
|
using Ghost.Data.Services;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
@@ -11,17 +12,24 @@ namespace Ghost.Editor.View.Pages.Landing;
|
|||||||
|
|
||||||
internal sealed partial class OpenProjectPage : Page
|
internal sealed partial class OpenProjectPage : Page
|
||||||
{
|
{
|
||||||
|
private readonly ProjectService _projectService;
|
||||||
|
|
||||||
public readonly ObservableCollection<ProjectInfo> projects = new();
|
public readonly ObservableCollection<ProjectInfo> projects = new();
|
||||||
|
|
||||||
public OpenProjectPage()
|
public OpenProjectPage()
|
||||||
{
|
{
|
||||||
foreach (var project in ProjectRepository.LoadProjects())
|
_projectService = App.GetService<ProjectService>();
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
await foreach (var project in _projectService.LoadProjectAsync())
|
||||||
{
|
{
|
||||||
projects.Add(project);
|
projects.Add(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
if (projects.Count == 0)
|
if (projects.Count == 0)
|
||||||
{
|
{
|
||||||
PlaceHolderText.Visibility = Visibility.Visible;
|
PlaceHolderText.Visibility = Visibility.Visible;
|
||||||
|
|||||||
14
Ghost.Editor/View/Windows/EngineEditorWindow.xaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<winex:WindowEx
|
||||||
|
x:Class="Ghost.Editor.View.Windows.EngineEditorWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:winex="using:WinUIEx"
|
||||||
|
Title="EngineEditorWindow"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Grid />
|
||||||
|
</winex:WindowEx>
|
||||||
16
Ghost.Editor/View/Windows/EngineEditorWindow.xaml.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using WinUIEx;
|
||||||
|
|
||||||
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
|
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||||
|
|
||||||
|
namespace Ghost.Editor.View.Windows;
|
||||||
|
/// <summary>
|
||||||
|
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class EngineEditorWindow : WindowEx
|
||||||
|
{
|
||||||
|
public EngineEditorWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,21 @@
|
|||||||
<MicaBackdrop />
|
<MicaBackdrop />
|
||||||
</Window.SystemBackdrop>
|
</Window.SystemBackdrop>
|
||||||
|
|
||||||
<Grid Padding="24,32,24,18">
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="32" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<TextBlock
|
||||||
|
Margin="24,0,0,0"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
|
Text="Ghost Engine" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Padding="24,0,24,18">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@@ -43,4 +57,5 @@
|
|||||||
Padding="8"
|
Padding="8"
|
||||||
IsNavigationStackEnabled="False" />
|
IsNavigationStackEnabled="False" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</winex:WindowEx>
|
</winex:WindowEx>
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Ghost.Database.Models.Projects;
|
using Ghost.Data.Models;
|
||||||
using Ghost.Editor.Constants;
|
using Ghost.Data.Services;
|
||||||
using Ghost.Editor.Contracts;
|
using Ghost.Editor.Contracts;
|
||||||
using Ghost.Editor.Helpers;
|
using Ghost.Editor.Helpers;
|
||||||
using Ghost.Editor.Models.Warpers;
|
|
||||||
using Microsoft.UI.Dispatching;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
namespace Ghost.Editor.ViewModel.Pages.Landing;
|
namespace Ghost.Editor.ViewModel.Pages.Landing;
|
||||||
|
|
||||||
internal partial class CreateProjectViewModel : ObservableRecipient, INavigationAware
|
internal partial class CreateProjectViewModel(ProjectService projectService) : ObservableRecipient, INavigationAware
|
||||||
{
|
{
|
||||||
public ObservableCollection<TemplateInfoWarper> templates = new();
|
public ObservableCollection<TemplateData> templates = new();
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial TemplateInfoWarper? SelectedTemplate
|
public partial TemplateData? SelectedTemplate
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
@@ -39,23 +37,14 @@ internal partial class CreateProjectViewModel : ObservableRecipient, INavigation
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNavigatedTo(object? parameter)
|
public async void OnNavigatedTo(object? parameter)
|
||||||
{
|
{
|
||||||
DispatcherQueue.GetForCurrentThread().TryEnqueue(DispatcherQueuePriority.High, () =>
|
await foreach (var (path, info) in projectService.GetProjectTemplatesAsync())
|
||||||
{
|
{
|
||||||
foreach (var templatePath in Directory.GetFiles(EditorDataPath.ProjectTemplatesFolder, "template.json", SearchOption.AllDirectories))
|
templates.Add(new(path, info));
|
||||||
{
|
|
||||||
var templateInfo = JsonSerializer.Deserialize<TemplateInfo>(File.ReadAllText(templatePath));
|
|
||||||
if (templateInfo == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
templates.Add(new(templatePath, templateInfo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedTemplate = templates.FirstOrDefault();
|
SelectedTemplate = templates.FirstOrDefault();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNavigatedFrom()
|
public void OnNavigatedFrom()
|
||||||
@@ -67,4 +56,18 @@ internal partial class CreateProjectViewModel : ObservableRecipient, INavigation
|
|||||||
{
|
{
|
||||||
ProjectLocation = (await SystemUtilities.OpenFolderPickerAsync())?.Path ?? string.Empty;
|
ProjectLocation = (await SystemUtilities.OpenFolderPickerAsync())?.Path ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task CreateProject()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ProjectName) || !Directory.Exists(ProjectLocation) || SelectedTemplate == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectPath = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, SelectedTemplate.directory);
|
||||||
|
|
||||||
|
var packageVersion = Package.Current.Id.Version;
|
||||||
|
await projectService.AddProjectAsync(ProjectName, projectPath, new System.Version(packageVersion.Major, packageVersion.Minor, packageVersion.Build));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Database")]
|
|
||||||
@@ -6,7 +6,16 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Folder Include="Resources\" />
|
||||||
<Folder Include="Services\" />
|
<Folder Include="Services\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
namespace Ghost.Engine.Models;
|
namespace Ghost.Engine.Models;
|
||||||
|
|
||||||
public class Component
|
public abstract class Component
|
||||||
{
|
{
|
||||||
|
public required GameEntity Owner
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
Ghost.Engine/Models/GameEntity.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Ghost.Engine.Models;
|
||||||
|
|
||||||
|
public abstract class GameEntity
|
||||||
|
{
|
||||||
|
private ObservableCollection<Component> _components = new();
|
||||||
|
|
||||||
|
public GameEntity()
|
||||||
|
{
|
||||||
|
//AddComponent(new Transform());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddComponent(Component component)
|
||||||
|
{
|
||||||
|
_components.Add(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using Ghost.Engine.Components;
|
|
||||||
|
|
||||||
namespace Ghost.Engine.Models;
|
|
||||||
|
|
||||||
public class GameObject
|
|
||||||
{
|
|
||||||
private List<Component> _components = new();
|
|
||||||
|
|
||||||
public GameObject()
|
|
||||||
{
|
|
||||||
AddComponent(new Transform());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddComponent(Component component)
|
|
||||||
{
|
|
||||||
_components.Add(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
Ghost.Entities/AssemblyInfo.cs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
global using EntityID = System.UInt32;
|
||||||
|
|
||||||
|
global using GenerationID = System.UInt16;
|
||||||
|
global using WorldID = System.UInt16;
|
||||||
88
Ghost.Entities/Core/Entity.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ghost.Entities.Core;
|
||||||
|
|
||||||
|
[SkipLocalsInit]
|
||||||
|
public struct Entity : IEquatable<Entity>, IComparable<Entity>
|
||||||
|
{
|
||||||
|
private const EntityID _WORLD_INDEX_BITS = 4u;
|
||||||
|
private const EntityID _GENERATION_BITS = 8u;
|
||||||
|
private const EntityID _INDEX_BITS = sizeof(EntityID) * 8 - _WORLD_INDEX_BITS - _GENERATION_BITS;
|
||||||
|
|
||||||
|
private const EntityID _WORLD_INDEX_MASK = (1u << (int)_WORLD_INDEX_BITS) - 1;
|
||||||
|
private const EntityID _GENERATION_MASK = (1u << (int)_GENERATION_BITS) - 1;
|
||||||
|
private const EntityID _INDEX_MASK = (1u << (int)_INDEX_BITS) - 1;
|
||||||
|
private const EntityID _ID_MASK = EntityID.MaxValue;
|
||||||
|
|
||||||
|
private uint _id;
|
||||||
|
|
||||||
|
public readonly bool IsValid
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _id != _ID_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly EntityID Index
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _id & _INDEX_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly GenerationID Generation
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => (GenerationID)((_id >> (int)_INDEX_BITS) & _GENERATION_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly WorldID WorldIndex
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => (WorldID)((_id >> (int)(_INDEX_BITS + _GENERATION_BITS)) & _WORLD_INDEX_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementGeneration()
|
||||||
|
{
|
||||||
|
var generation = Generation + 1u;
|
||||||
|
if (generation >= _GENERATION_MASK)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Generation overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
_id = (_id & ~(_GENERATION_MASK << (int)_INDEX_BITS)) | (generation << (int)_INDEX_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Entity(EntityID index, EntityID generation, EntityID worldIndex)
|
||||||
|
{
|
||||||
|
_id = (worldIndex << (int)(_INDEX_BITS + _GENERATION_BITS)) | (generation << (int)_INDEX_BITS) | index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(Entity other)
|
||||||
|
{
|
||||||
|
return _id == other._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly int CompareTo(Entity other)
|
||||||
|
{
|
||||||
|
return _id.CompareTo(other._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is Entity other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly int GetHashCode()
|
||||||
|
{
|
||||||
|
return _id.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Entity left, Entity right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(Entity left, Entity right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
Ghost.Entities/Core/EntityInfo.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Ghost.Entities.Core;
|
||||||
|
|
||||||
|
public readonly struct EntityInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
141
Ghost.Entities/Core/World.cs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
using Ghost.Entities.Helpers;
|
||||||
|
using Misaki.HighPerformance.Unsafe.Collections;
|
||||||
|
|
||||||
|
namespace Ghost.Entities.Core;
|
||||||
|
|
||||||
|
public partial struct World
|
||||||
|
{
|
||||||
|
public static UnsafeArray<World> Worlds
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
} = new(4, AllocationType.UnInitialized);
|
||||||
|
|
||||||
|
public static UnsafeQueue<WorldID> FreeIndices
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
} = new(4, AllocationType.UnInitialized);
|
||||||
|
|
||||||
|
public static ushort Count
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static World Create(int chunkSizeInBytes = 16384, int minimumAmountOfEntitiesPerChunk = 100, int archetypeCapacity = 2, int entityCapacity = 64)
|
||||||
|
{
|
||||||
|
lock (ThreadLocker.WorldLock)
|
||||||
|
{
|
||||||
|
var recycle = FreeIndices.TryDequeue(out var id);
|
||||||
|
var recycledId = recycle ? id : Count;
|
||||||
|
|
||||||
|
var world = new World(recycledId, chunkSizeInBytes, minimumAmountOfEntitiesPerChunk, archetypeCapacity, entityCapacity);
|
||||||
|
|
||||||
|
if (recycledId >= Worlds.Size)
|
||||||
|
{
|
||||||
|
var newCapacity = Worlds.Size * 2;
|
||||||
|
Worlds.ReAlloc(newCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Worlds[recycledId] = world;
|
||||||
|
Count++;
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial struct World
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The unique <see cref="World"/> ID.
|
||||||
|
/// </summary>
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of <see cref="Entity"/>s currently stored by this <see cref="World"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int Size
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The available <see cref="Entity"/> capacity of this <see cref="World"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// All <see cref="Archetype"/>s that exist in this <see cref="World"/>.
|
||||||
|
///// </summary>
|
||||||
|
//public Archetypes Archetypes
|
||||||
|
//{
|
||||||
|
// get;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Maps an <see cref="Entity"/> to its <see cref="EntityInfo"/> for quick lookup.
|
||||||
|
///// </summary>
|
||||||
|
//internal EntityInfoStorage EntityInfo
|
||||||
|
//{
|
||||||
|
// get;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Stores recycled <see cref="Entity"/> IDs and their last version.
|
||||||
|
///// </summary>
|
||||||
|
//internal PooledQueue<RecycledEntity> RecycledIds
|
||||||
|
//{
|
||||||
|
// get; set;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// A cache to map <see cref="QueryDescription"/> to their <see cref="Core.Query"/>, to avoid allocs.
|
||||||
|
///// </summary>
|
||||||
|
//internal PooledDictionary<QueryDescription, Query> QueryCache
|
||||||
|
//{
|
||||||
|
// get; set;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Chunk"/> size of each <see cref="Archetype"/> in bytes.
|
||||||
|
/// <remarks>For the best cache optimisation use values that are divisible by 16Kb.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public int BaseChunkSize { get; private set; } = 16_384;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum number of <see cref="Arch.Core.Entity"/>'s that should fit into a <see cref="Chunk"/> within all <see cref="Archetype"/>s.
|
||||||
|
/// On the basis of this, the <see cref="Archetypes"/>s chunk size may increase.
|
||||||
|
/// </summary>
|
||||||
|
public int BaseChunkEntityCount { get; private set; } = 100;
|
||||||
|
|
||||||
|
private World(int id, int baseChunkSize, int baseChunkEntityCount, int archetypeCapacity, int entityCapacity)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
|
||||||
|
// Mapping.
|
||||||
|
//GroupToArchetype = new PooledDictionary<int, Archetype>(archetypeCapacity);
|
||||||
|
|
||||||
|
// Entity stuff.
|
||||||
|
//Archetypes = new Archetypes(archetypeCapacity);
|
||||||
|
//EntityInfo = new EntityInfoStorage(baseChunkSize, entityCapacity);
|
||||||
|
//RecycledIds = new PooledQueue<RecycledEntity>(entityCapacity);
|
||||||
|
|
||||||
|
// Query.
|
||||||
|
//QueryCache = new PooledDictionary<QueryDescription, Query>(archetypeCapacity);
|
||||||
|
|
||||||
|
// Multithreading/Jobs.
|
||||||
|
//JobHandles = new PooledList<JobHandle>(Environment.ProcessorCount);
|
||||||
|
//JobsCache = new List<IJob>(Environment.ProcessorCount);
|
||||||
|
|
||||||
|
// Config
|
||||||
|
BaseChunkSize = baseChunkSize;
|
||||||
|
BaseChunkEntityCount = baseChunkEntityCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Ghost.Entities/Ghost.Entities.csproj
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||||
|
<HintPath>..\..\source\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
7
Ghost.Entities/Helpers/ThreadLocker.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Ghost.Entities.Helpers;
|
||||||
|
|
||||||
|
internal static class ThreadLocker
|
||||||
|
{
|
||||||
|
private static Lock? _worldLock;
|
||||||
|
public static Lock WorldLock => _worldLock ??= new();
|
||||||
|
}
|
||||||
@@ -7,7 +7,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor", "Ghost.Edito
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Database", "Ghost.Database\Ghost.Database.csproj", "{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Data", "Ghost.Data\Ghost.Data.csproj", "{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Entities", "Ghost.Entities\Ghost.Entities.csproj", "{329CA048-4BA8-3E60-8546-E448746D4444}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -61,6 +63,18 @@ Global
|
|||||||
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x64.Build.0 = Release|Any CPU
|
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x86.ActiveCfg = Release|Any CPU
|
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x86.Build.0 = Release|Any CPU
|
{0D626DAF-EF18-435C-A85C-EEA1B141E8B5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|ARM64.Build.0 = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{329CA048-4BA8-3E60-8546-E448746D4444}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||