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.
This commit is contained in:
2025-03-27 00:52:07 +09:00
parent 02b3edcd7a
commit 62fe30ff2b
47 changed files with 711 additions and 231 deletions

View File

@@ -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)!);
}

View File

@@ -1,8 +0,0 @@
namespace Ghost.Core.Activation;
public interface IActivationHandler
{
bool CanHandle(object args);
Task HandleAsync(object args);
}

View File

@@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -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();
} }
} }

View File

@@ -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
View 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
{
}

View File

@@ -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
{ {

View File

@@ -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";

View File

@@ -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");
} }

View 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();
}
}

View File

@@ -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;
}
}

View 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();
}
}

View File

@@ -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();

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -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");
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>();
} }
} }

View File

@@ -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>

View File

@@ -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="&#xE712;" />
</Button>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>

View File

@@ -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;

View 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>

View 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();
}
}

View File

@@ -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>

View File

@@ -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));
}
} }

View File

@@ -1,4 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Editor")]
[assembly: InternalsVisibleTo("Ghost.Database")]

View File

@@ -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>

View File

@@ -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;
}
} }

View 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);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,4 @@
global using EntityID = System.UInt32;
global using GenerationID = System.UInt16;
global using WorldID = System.UInt16;

View 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);
}
}

View File

@@ -0,0 +1,5 @@
namespace Ghost.Entities.Core;
public readonly struct EntityInfo
{
}

View 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;
}
}

View 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>

View File

@@ -0,0 +1,7 @@
namespace Ghost.Entities.Helpers;
internal static class ThreadLocker
{
private static Lock? _worldLock;
public static Lock WorldLock => _worldLock ??= new();
}

View File

@@ -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