feat: initial commit
This commit is contained in:
commit
e9dad5bbe4
29 changed files with 15100 additions and 0 deletions
122
.editorconfig
Normal file
122
.editorconfig
Normal file
|
@ -0,0 +1,122 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers=false
|
||||
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
csharp_space_after_cast=false
|
||||
csharp_style_var_elsewhere=false:hint
|
||||
csharp_style_var_for_built_in_types=false:hint
|
||||
csharp_style_var_when_type_is_apparent=true:hint
|
||||
csharp_preserve_single_line_statements=false
|
||||
csharp_preserve_single_line_blocks=true
|
||||
dotnet_style_predefined_type_for_locals_parameters_members=true:hint
|
||||
dotnet_style_predefined_type_for_member_access=true:hint
|
||||
dotnet_style_qualification_for_event=true:hint
|
||||
dotnet_style_qualification_for_field=true:hint
|
||||
dotnet_style_qualification_for_method=true:hint
|
||||
dotnet_style_qualification_for_property=true:hint
|
||||
dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint
|
||||
|
||||
# ReSharper properties
|
||||
resharper_alignment_tab_fill_style=optimal_fill
|
||||
resharper_apply_on_completion=true
|
||||
resharper_blank_lines_after_control_transfer_statements=1
|
||||
resharper_blank_lines_around_single_line_auto_property=1
|
||||
resharper_blank_lines_around_single_line_property=1
|
||||
resharper_blank_lines_before_single_line_comment=1
|
||||
resharper_blank_lines_between_using_groups=1
|
||||
resharper_braces_for_for=required
|
||||
resharper_braces_for_foreach=required
|
||||
resharper_braces_for_ifelse=required
|
||||
resharper_braces_for_while=required
|
||||
resharper_can_use_global_alias=false
|
||||
resharper_csharp_blank_lines_around_single_line_field=1
|
||||
resharper_csharp_blank_lines_around_single_line_invocable=1
|
||||
resharper_csharp_indent_style=tab
|
||||
resharper_csharp_insert_final_newline=true
|
||||
resharper_csharp_keep_blank_lines_in_code=1
|
||||
resharper_csharp_keep_blank_lines_in_declarations=1
|
||||
resharper_csharp_new_line_before_while=true
|
||||
resharper_csharp_use_indent_from_vs=false
|
||||
resharper_csharp_wrap_arguments_style=chop_if_long
|
||||
resharper_csharp_wrap_extends_list_style=chop_if_long
|
||||
resharper_csharp_wrap_parameters_style=chop_if_long
|
||||
resharper_css_insert_final_newline=false
|
||||
resharper_enforce_line_ending_style=true
|
||||
resharper_html_insert_final_newline=false
|
||||
resharper_indent_nested_fixed_stmt=true
|
||||
resharper_js_indent_style=tab
|
||||
resharper_js_insert_final_newline=true
|
||||
resharper_js_keep_blank_lines_in_code=1
|
||||
resharper_js_stick_comment=false
|
||||
resharper_js_use_indent_from_vs=false
|
||||
resharper_js_wrap_before_binary_opsign=true
|
||||
resharper_js_wrap_chained_method_calls=chop_if_long
|
||||
resharper_keep_blank_lines_between_declarations=1
|
||||
resharper_min_blank_lines_after_imports=1
|
||||
resharper_place_attribute_on_same_line=False
|
||||
resharper_place_constructor_initializer_on_same_line=false
|
||||
resharper_place_type_constraints_on_same_line=false
|
||||
resharper_protobuf_insert_final_newline=false
|
||||
resharper_qualified_using_at_nested_scope=true
|
||||
resharper_resx_insert_final_newline=false
|
||||
resharper_space_within_single_line_array_initializer_braces=true
|
||||
resharper_use_indents_from_main_language_in_file=false
|
||||
resharper_vb_insert_final_newline=false
|
||||
resharper_wrap_after_declaration_lpar=true
|
||||
resharper_wrap_after_invocation_lpar=true
|
||||
resharper_wrap_before_extends_colon=true
|
||||
resharper_wrap_before_first_type_parameter_constraint=true
|
||||
resharper_wrap_before_type_parameter_langle=true
|
||||
resharper_xmldoc_indent_child_elements=ZeroIndent
|
||||
resharper_xmldoc_indent_text=ZeroIndent
|
||||
resharper_xmldoc_insert_final_newline=false
|
||||
resharper_xml_insert_final_newline=false
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_check_namespace_highlighting=none
|
||||
resharper_convert_to_auto_property_highlighting=none
|
||||
resharper_localizable_element_highlighting=none
|
||||
resharper_redundant_comma_in_attribute_list_highlighting=none
|
||||
resharper_redundant_comma_in_enum_declaration_highlighting=none
|
||||
resharper_redundant_comma_in_initializer_highlighting=none
|
||||
resharper_string_compare_to_is_culture_specific_highlighting=none
|
||||
resharper_string_index_of_is_culture_specific_1_highlighting=none
|
||||
resharper_use_null_propagation_highlighting=none
|
||||
resharper_use_object_or_collection_initializer_highlighting=hint
|
||||
resharper_use_string_interpolation_highlighting=hint
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
||||
|
||||
[*.{cs,js,json,jsx,proto,resjson,ts,tsx}]
|
||||
indent_style=space
|
||||
indent_size=space
|
||||
tab_width=4
|
||||
|
||||
[*.{asax,ascx,aspx,cshtml,css,htm,html,master,razor,skin,vb,xaml,xamlx,xoml}]
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
tab_width=4
|
||||
|
||||
[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,jsproj,lsproj,njsproj,nuspec,proj,props,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
||||
|
||||
[*.proto]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use asdf
|
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
launchSettings.json
|
||||
|
||||
*~
|
||||
*.user
|
||||
Directory.Build.props
|
||||
|
||||
obj/
|
||||
[Bb]in/
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
_ReSharper.Caches/
|
||||
node_modules/
|
51
.gitlab-ci.yml
Normal file
51
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
stages:
|
||||
- build
|
||||
|
||||
default:
|
||||
before_script:
|
||||
- curl -sL https://deb.nodesource.com/setup_15.x | bash -
|
||||
- apt-get install -y nodejs
|
||||
|
||||
build:
|
||||
image: mcr.microsoft.com/dotnet/sdk:5.0
|
||||
stage: build
|
||||
script:
|
||||
# Set up the environment.
|
||||
- npx npm install --ci
|
||||
- npx commitlint-gitlab-ci -x @commitlint/config-conventional
|
||||
|
||||
# Build and test everything.
|
||||
- dotnet restore
|
||||
- dotnet build
|
||||
- 'dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/{assembly}-test-result.xml;MethodFormat=Default;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"'
|
||||
|
||||
# Summarize the output for Gitlab CI reporting.
|
||||
- dotnet new tool-manifest
|
||||
- dotnet tool install dotnet-reportgenerator-globaltool
|
||||
- dotnet tool run reportgenerator -reports:src/*/TestResults/*/coverage.cobertura.xml -targetdir:./coverage "-reporttypes:Cobertura;TextSummary"
|
||||
- grep "Line coverage" coverage/Summary.txt
|
||||
|
||||
# Perform the release.
|
||||
- npx semantic-release
|
||||
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TITLE =~ /^chore\(release\)/'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
|
||||
when: never
|
||||
- when: on_success
|
||||
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ./**/*test-result.xml
|
||||
- ./coverage/Cobertura.xml
|
||||
- ./coverage/Summary.*
|
||||
- ./**/*.nupkg
|
||||
reports:
|
||||
junit:
|
||||
- ./**/*test-result.xml
|
||||
cobertura:
|
||||
- ./coverage/Cobertura.xml
|
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
3
.tool-versions
Normal file
3
.tool-versions
Normal file
|
@ -0,0 +1,3 @@
|
|||
dotnet-core 5.0.100
|
||||
yarn 1.22.10
|
||||
nodejs 15.0.1
|
29
CHANGELOG.md
Normal file
29
CHANGELOG.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
## [1.1.4](https://gitlab.com/mfgames-cil/mfgames-locking-cil/compare/v1.1.3...v1.1.4) (2021-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **semantic-release-nuget:** fixing typo ([4ce4fe2](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/4ce4fe26cb3d8e90a7ef710a82626af8e7caa147))
|
||||
|
||||
## [1.1.3](https://gitlab.com/mfgames-cil/mfgames-locking-cil/compare/v1.1.2...v1.1.3) (2021-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* working on pushing ([5c66619](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/5c66619e12587ada01c5eafd5a4aa5d8f6ef3c5d))
|
||||
|
||||
## [1.1.2](https://gitlab.com/mfgames-cil/mfgames-locking-cil/compare/v1.1.1...v1.1.2) (2021-09-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* updating package-lock.json ([d000882](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/d000882f96895ffa42c000f0169048354db78bc1))
|
||||
* working on build patterns ([7594b73](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/7594b73846b80127ee6ba7ab1d3d0f6d64c81cc5))
|
||||
* working on dependencies ([ba538c0](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/ba538c0d86005618c84f3fc6bbf062d60e499a1b))
|
||||
|
||||
## [1.1.1](https://gitlab.com/mfgames-cil/mfgames-locking-cil/compare/v1.1.0...v1.1.1) (2021-01-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* added changelog generation ([d8fa302](https://gitlab.com/mfgames-cil/mfgames-locking-cil/commit/d8fa3029a9c2d0c9523584a78e16d4612fd114f8))
|
54
Gallium.sln
Normal file
54
Gallium.sln
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26124.0
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4C5CBAB6-6400-4C7C-B192-B28F07DD722B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gallium", "src\Gallium\Gallium.csproj", "{940230CF-253F-4DA2-95FA-9DFB77089D4F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gallium.Tests", "src\Gallium.Tests\Gallium.Tests.csproj", "{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{940230CF-253F-4DA2-95FA-9DFB77089D4F} = {4C5CBAB6-6400-4C7C-B192-B28F07DD722B}
|
||||
{D6E5D0F6-8CDB-40B5-96D4-8719FC724AE5} = {4C5CBAB6-6400-4C7C-B192-B28F07DD722B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
1370
Gallium.sln.DotSettings
Normal file
1370
Gallium.sln.DotSettings
Normal file
File diff suppressed because it is too large
Load diff
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Moonfire Games
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
Gallium CIL
|
||||
===========
|
||||
|
||||
A small Entity-Component-System (ECS) that is built around LINQ calls and IEnumerable<Entity> objects.
|
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
};
|
12308
package-lock.json
generated
Normal file
12308
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "gallium-cil",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^13.1.0",
|
||||
"@commitlint/config-conventional": "^13.1.0",
|
||||
"@semantic-release/changelog": "^5.0.1",
|
||||
"@semantic-release/git": "^9.0.0",
|
||||
"@semantic-release/gitlab": "^6.2.2",
|
||||
"@semantic-release/npm": "^7.1.3",
|
||||
"commitlint-gitlab-ci": "^0.0.4",
|
||||
"husky": "^7.0.2",
|
||||
"semantic-release": "^17.4.7",
|
||||
"semantic-release-dotnet": "^1.0.0",
|
||||
"semantic-release-nuget": "^1.1.0"
|
||||
}
|
||||
}
|
20
release.config.js
Normal file
20
release.config.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
branches: ["main"],
|
||||
message: "chore(release): v${nextRelease.version}\n\n${nextRelease.notes}",
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/npm",
|
||||
"semantic-release-dotnet",
|
||||
[
|
||||
"semantic-release-nuget",
|
||||
{
|
||||
packArguments: ["--include-symbols", "--include-source"],
|
||||
pushFiles: ["bin/*.nupkg"],
|
||||
},
|
||||
],
|
||||
"@semantic-release/changelog",
|
||||
"@semantic-release/git",
|
||||
"@semantic-release/gitlab",
|
||||
],
|
||||
};
|
213
src/Gallium.Tests/EntityTests.cs
Normal file
213
src/Gallium.Tests/EntityTests.cs
Normal file
|
@ -0,0 +1,213 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Gallium.Tests
|
||||
{
|
||||
public class EntityTests : GalliumTestsBase
|
||||
{
|
||||
public EntityTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddingComponentOnceWorks()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.True(entity1.Has<TestComponent1>());
|
||||
Assert.Equal(component1, entity1.Get<TestComponent1>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddingComponentViaInterfaceWork()
|
||||
{
|
||||
var component1 = new TestComponent3a();
|
||||
Entity entity1 = new Entity().Add<ITestComponent3>(component1);
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.False(entity1.Has<TestComponent3a>());
|
||||
Assert.True(entity1.Has<ITestComponent3>());
|
||||
Assert.Equal(component1, entity1.Get<ITestComponent3>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddingTwiceThrowsException()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Exception exception = Assert.Throws<ArgumentException>(
|
||||
() => new Entity()
|
||||
.Add(component1)
|
||||
.Add(component1));
|
||||
|
||||
Assert.Equal(
|
||||
"An element with the same type "
|
||||
+ "(Gallium.Tests.TestComponent1)"
|
||||
+ " already exists. (Parameter 'component')",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyEntityAlsoCopiesComponents()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
Entity entity2 = entity1.Copy();
|
||||
|
||||
Assert.Equal(1, entity2.Count);
|
||||
Assert.True(entity2.Has<TestComponent1>());
|
||||
Assert.Equal(component1, entity2.Get<TestComponent1>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExactCopyEntityAlsoCopiesComponents()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
Entity entity2 = entity1.ExactCopy();
|
||||
|
||||
Assert.Equal(1, entity2.Count);
|
||||
Assert.True(entity2.Has<TestComponent1>());
|
||||
Assert.Equal(component1, entity2.Get<TestComponent1>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOptionalWorks()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
|
||||
Assert.Equal(component1, entity1.GetOptional<TestComponent1>());
|
||||
Assert.Null(entity1.GetOptional<TestComponent2>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasReturnsFalseIfNotRegistered()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
|
||||
Assert.False(entity1.Has<TestComponent1>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewEntityHasNoComponents()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
|
||||
Assert.Equal(0, entity1.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveAlreadyMissingComponentWorks()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
|
||||
Assert.Equal(0, entity1.Count);
|
||||
|
||||
Entity entity2 = entity1.Remove<TestComponent1>();
|
||||
|
||||
Assert.Equal(0, entity1.Count);
|
||||
Assert.Equal(0, entity2.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveComponentDoesNotRemoveFromCopies()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
Entity entity2 = entity1.ExactCopy();
|
||||
Entity entity3 = entity1.Copy();
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.Equal(1, entity2.Count);
|
||||
Assert.Equal(1, entity3.Count);
|
||||
|
||||
Entity entity1A = entity1.Remove<TestComponent1>();
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.Equal(0, entity1A.Count);
|
||||
Assert.Equal(1, entity2.Count);
|
||||
Assert.Equal(1, entity3.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveComponentWorks()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
|
||||
Entity entity2 = entity1.Remove<TestComponent1>();
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.Equal(0, entity2.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SettingComponentsWorks()
|
||||
{
|
||||
var component1 = new TestComponent3a();
|
||||
var component2 = new TestComponent3b();
|
||||
Entity entity1 = new Entity()
|
||||
.Set<ITestComponent3>(component1)
|
||||
.Set<ITestComponent3>(component1)
|
||||
.Set<ITestComponent3>(component2);
|
||||
|
||||
Assert.Equal(1, entity1.Count);
|
||||
Assert.Equal(component2, entity1.Get<ITestComponent3>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetWorks()
|
||||
{
|
||||
var component1 = new TestComponent1();
|
||||
Entity entity1 = new Entity().Add(component1);
|
||||
|
||||
bool result1 = entity1.TryGet(out TestComponent1 value1);
|
||||
|
||||
Assert.True(result1);
|
||||
Assert.Equal(component1, value1);
|
||||
|
||||
bool result2 = entity1.TryGet(out TestComponent2 _);
|
||||
|
||||
Assert.False(result2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TwoCopiesHaveDifferentIds()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
Entity entity2 = entity1.Copy();
|
||||
|
||||
Assert.NotEqual(entity1.Id, entity2.Id);
|
||||
Assert.NotEqual(entity1, entity2);
|
||||
Assert.False(entity1 == entity2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TwoEntitiesHaveDifferentIds()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
var entity2 = new Entity();
|
||||
|
||||
Assert.NotEqual(entity1.Id, entity2.Id);
|
||||
Assert.NotEqual(entity1, entity2);
|
||||
Assert.False(entity1 == entity2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TwoExactCopiesHaveDifferentIds()
|
||||
{
|
||||
var entity1 = new Entity();
|
||||
Entity entity2 = entity1.ExactCopy();
|
||||
|
||||
Assert.Equal(entity1.Id, entity2.Id);
|
||||
Assert.Equal(entity1, entity2);
|
||||
Assert.True(entity1 == entity2);
|
||||
}
|
||||
}
|
||||
}
|
242
src/Gallium.Tests/EnumerableEntityTests.cs
Normal file
242
src/Gallium.Tests/EnumerableEntityTests.cs
Normal file
|
@ -0,0 +1,242 @@
|
|||
using System.Linq;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Gallium.Tests
|
||||
{
|
||||
public class EnumerableEntityTests
|
||||
{
|
||||
[Fact]
|
||||
public void ForComponentsC1()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add(new TestComponent1()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1!", "2", "3!" },
|
||||
entities
|
||||
.ForEachEntity<TestComponent1>(
|
||||
(e, _) => e.Set(e.Get<string>() + "!"))
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForComponentsC2()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add(new TestComponent1())
|
||||
.Add(new TestComponent2()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1", "2", "3!" },
|
||||
entities
|
||||
.ForEachEntity<TestComponent1, TestComponent2>(
|
||||
(e, _, _) => e.Set(e.Get<string>() + "!"))
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForComponentsC3()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2())
|
||||
.Add<ITestComponent3>(new TestComponent3b()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add<ITestComponent3>(new TestComponent3a())
|
||||
.Add(new TestComponent1())
|
||||
.Add(new TestComponent2()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1", "2", "3-TestComponent3a" },
|
||||
entities
|
||||
.ForEachEntity<TestComponent1, TestComponent2,
|
||||
ITestComponent3>(
|
||||
(e, _, _, t) => e.Set(
|
||||
e.Get<string>() + "-" + t.GetType().Name))
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasComponentsC1()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add(new TestComponent1()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1", "3" },
|
||||
entities
|
||||
.HasComponents<TestComponent1>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasComponentsC2()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2())
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add(new TestComponent1()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "2" },
|
||||
entities
|
||||
.HasComponents<TestComponent1, TestComponent2>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasComponentsC3()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1())
|
||||
.Add<ITestComponent3>(new TestComponent3b())
|
||||
.Add(new TestComponent2()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2())
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add<ITestComponent3>(new TestComponent3a()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1" },
|
||||
entities
|
||||
.HasComponents<TestComponent1, TestComponent2,
|
||||
ITestComponent3>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotComponentsC1()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add(new TestComponent1()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "2" },
|
||||
entities
|
||||
.NotComponents<TestComponent1>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotComponentsC2()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent2())
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("3"),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new[] { "1", "3" },
|
||||
entities
|
||||
.NotComponents<TestComponent1, TestComponent2>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotComponentsC3()
|
||||
{
|
||||
Entity[] entities =
|
||||
{
|
||||
new Entity()
|
||||
.Add("1")
|
||||
.Add(new TestComponent1()),
|
||||
new Entity()
|
||||
.Add("2")
|
||||
.Add(new TestComponent1())
|
||||
.Add(new TestComponent2())
|
||||
.Add<ITestComponent3>(new TestComponent3b()),
|
||||
new Entity()
|
||||
.Add("3")
|
||||
.Add<ITestComponent3>(new TestComponent3a()),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
new string[] { "1", "3" },
|
||||
entities
|
||||
.NotComponents<TestComponent1, TestComponent2,
|
||||
ITestComponent3>()
|
||||
.Select(x => x.Get<string>())
|
||||
.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
28
src/Gallium.Tests/Gallium.Tests.csproj
Normal file
28
src/Gallium.Tests/Gallium.Tests.csproj
Normal file
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Gallium.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.XUnit" Version="2.0.4"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Gallium\Gallium.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
40
src/Gallium.Tests/GalliumTestsBase.cs
Normal file
40
src/Gallium.Tests/GalliumTestsBase.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Gallium.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Common initialization logic for Gallium-based tests including setting
|
||||
/// up containers, logging, and Serilog.
|
||||
/// </summary>
|
||||
public abstract class GalliumTestsBase
|
||||
{
|
||||
protected GalliumTestsBase(ITestOutputHelper output)
|
||||
{
|
||||
this.Output = output;
|
||||
|
||||
// Set up logging.
|
||||
const string Template =
|
||||
"[{Level:u3}] "
|
||||
+ "({SourceContext}) {Message}"
|
||||
+ "{NewLine}{Exception}";
|
||||
|
||||
this.Logger = new LoggerConfiguration()
|
||||
.WriteTo.TestOutput(
|
||||
output,
|
||||
outputTemplate: Template)
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output for the tests.
|
||||
/// </summary>
|
||||
public ITestOutputHelper Output { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger used to report messages about the test.
|
||||
/// </summary>
|
||||
protected Logger Logger { get; }
|
||||
}
|
||||
}
|
6
src/Gallium.Tests/ITestComponent3.cs
Normal file
6
src/Gallium.Tests/ITestComponent3.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Gallium.Tests
|
||||
{
|
||||
public interface ITestComponent3
|
||||
{
|
||||
}
|
||||
}
|
6
src/Gallium.Tests/TestComponent1.cs
Normal file
6
src/Gallium.Tests/TestComponent1.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Gallium.Tests
|
||||
{
|
||||
public class TestComponent1
|
||||
{
|
||||
}
|
||||
}
|
6
src/Gallium.Tests/TestComponent2.cs
Normal file
6
src/Gallium.Tests/TestComponent2.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Gallium.Tests
|
||||
{
|
||||
public class TestComponent2
|
||||
{
|
||||
}
|
||||
}
|
6
src/Gallium.Tests/TestComponent3a.cs
Normal file
6
src/Gallium.Tests/TestComponent3a.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Gallium.Tests
|
||||
{
|
||||
public class TestComponent3a : ITestComponent3
|
||||
{
|
||||
}
|
||||
}
|
6
src/Gallium.Tests/TestComponent3b.cs
Normal file
6
src/Gallium.Tests/TestComponent3b.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Gallium.Tests
|
||||
{
|
||||
public class TestComponent3b : ITestComponent3
|
||||
{
|
||||
}
|
||||
}
|
300
src/Gallium/Entity.cs
Normal file
300
src/Gallium/Entity.cs
Normal file
|
@ -0,0 +1,300 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading;
|
||||
|
||||
namespace Gallium
|
||||
{
|
||||
/// <summary>
|
||||
/// A low-overhead entity with identification.
|
||||
/// </summary>
|
||||
public record Entity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public virtual bool Equals(Entity? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.Id == other.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Id;
|
||||
}
|
||||
|
||||
private ImmutableDictionary<Type, object> Components { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal ID to ensure the entities are unique. Since we are not
|
||||
/// worried about serialization or using the identifiers from one call
|
||||
/// to another, we can use a simple interlocked identifier instead of
|
||||
/// a factory or provider method.
|
||||
/// </summary>
|
||||
private static int nextId;
|
||||
|
||||
public Entity()
|
||||
: this(Interlocked.Increment(ref nextId))
|
||||
{
|
||||
}
|
||||
|
||||
private Entity(int id)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Components = ImmutableDictionary.Create<Type, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entity has a specific type of
|
||||
/// component registered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>True if the type exists, otherwise false.</returns>
|
||||
public bool Has<TType>()
|
||||
{
|
||||
return this.Has(typeof(TType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entity has a specific type of
|
||||
/// component registered.
|
||||
/// </summary>
|
||||
/// <param name="type">The component type.</param>
|
||||
/// <returns>True if the type exists, otherwise false.</returns>
|
||||
public bool Has(Type type)
|
||||
{
|
||||
return this.Components.ContainsKey(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a registered component of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>The registered object.</returns>
|
||||
public TType Get<TType>()
|
||||
{
|
||||
return (TType)this.Components[typeof(TType)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a registered component of the given type and casts it to
|
||||
/// TType.
|
||||
/// </summary>
|
||||
/// <param name="type">The component key.</param>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>The registered object.</returns>
|
||||
public TType Get<TType>(Type type)
|
||||
{
|
||||
return (TType)this.Components[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of components registered in the entity.
|
||||
/// </summary>
|
||||
public int Count => this.Components.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the given component type if inside the entity, otherwise the
|
||||
/// default avlue.
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>The found component or default (typically null).</returns>
|
||||
public TType? GetOptional<TType>()
|
||||
{
|
||||
return this.Has<TType>() ? this.Get<TType>() : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the value, if present. If not, this returns false
|
||||
/// and the value is undefined. Otherwise, this method returns true
|
||||
/// and the actual value inside that variable.
|
||||
/// </summary>
|
||||
/// <param name="value">The value if contained in the entity.</param>
|
||||
/// <typeparam name="T1">The component type.</typeparam>
|
||||
/// <returns>True if found, otherwise false.</returns>
|
||||
public bool TryGet<T1>(out T1 value)
|
||||
{
|
||||
if (this.Has<T1>())
|
||||
{
|
||||
value = this.Get<T1>();
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the values, if present. If not, this returns false
|
||||
/// and the value is undefined. Otherwise, this method returns true
|
||||
/// and the actual value inside that variable.
|
||||
/// </summary>
|
||||
/// <param name="value1">The value if contained in the entity.</param>
|
||||
/// <param name="value2">The value if contained in the entity.</param>
|
||||
/// <typeparam name="T1">The first component type.</typeparam>
|
||||
/// <typeparam name="T2">The second component type.</typeparam>
|
||||
/// <returns>True if found, otherwise false.</returns>
|
||||
public bool TryGet<T1, T2>(out T1 value1, out T2 value2)
|
||||
{
|
||||
if (this.Has<T1>() && this.Has<T2>())
|
||||
{
|
||||
value1 = this.Get<T1>();
|
||||
value2 = this.Get<T2>();
|
||||
return true;
|
||||
}
|
||||
|
||||
value1 = default!;
|
||||
value2 = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the component in the entity, regardless if there was a
|
||||
/// component already registered.
|
||||
/// </summary>
|
||||
/// <param name="component">The component to register.</param>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>The entity for chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public Entity Set<TType>(TType component)
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(component));
|
||||
}
|
||||
|
||||
if (this.Components.TryGetValue(typeof(TType), out object? value)
|
||||
&& value is TType
|
||||
&& value.Equals(component))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return this with
|
||||
{
|
||||
Components = this.Components.SetItem(typeof(TType), component),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component to the entity.
|
||||
/// </summary>
|
||||
/// <param name="component">The component to register.</param>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>
|
||||
/// The same entity if the component is already registered, otherwise a
|
||||
/// cloned entity with the new component.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public Entity Add<TType>(TType component)
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(component));
|
||||
}
|
||||
|
||||
if (this.Has<TType>())
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"An element with the same type ("
|
||||
+ typeof(TType).FullName
|
||||
+ ") already exists.",
|
||||
nameof(component));
|
||||
}
|
||||
|
||||
if (this.Components.TryGetValue(typeof(TType), out object? value)
|
||||
&& value is TType
|
||||
&& value.Equals(component))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return this with
|
||||
{
|
||||
Components = this.Components.Add(typeof(TType), component),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a component to the entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">The component type.</typeparam>
|
||||
/// <returns>
|
||||
/// The same entity if the component is already removed, otherwise a
|
||||
/// cloned entity without the new component.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public Entity Remove<TType>()
|
||||
{
|
||||
return this.Remove(typeof(TType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a component to the entity.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The same entity if the component is already removed, otherwise a
|
||||
/// cloned entity without the new component.
|
||||
/// </returns>
|
||||
/// <param name="type">The component type to remove.</param>
|
||||
public Entity Remove(Type type)
|
||||
{
|
||||
if (!this.Has(type))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return this with
|
||||
{
|
||||
Components = this.Components.Remove(type),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the entity. This should be treated as an
|
||||
/// opaque field.
|
||||
/// </summary>
|
||||
public int Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the entity, including copying the identifier.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Entity ExactCopy()
|
||||
{
|
||||
return this with { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the entity, including components, but with a new
|
||||
/// identifier.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Entity Copy()
|
||||
{
|
||||
return this with
|
||||
{
|
||||
Id = Interlocked.Increment(ref nextId),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of the component types currently registered in the
|
||||
/// Entity.
|
||||
/// </summary>
|
||||
/// <returns>An enumerable of the various component keys.</returns>
|
||||
public IEnumerable<Type> GetComponentTypes()
|
||||
{
|
||||
return this.Components.Keys;
|
||||
}
|
||||
}
|
||||
}
|
109
src/Gallium/EnumerableEntityExtensions.cs
Normal file
109
src/Gallium/EnumerableEntityExtensions.cs
Normal file
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EE = System.Collections.Generic.IEnumerable<Gallium.Entity>;
|
||||
|
||||
namespace Gallium
|
||||
{
|
||||
public static class EnumerableEntityExtensions
|
||||
{
|
||||
public static EE ForEachEntity<T1, T2>(
|
||||
this EE entities,
|
||||
Func<Entity, T1, T2, Entity> lambda)
|
||||
{
|
||||
return entities
|
||||
.Select(
|
||||
x => !x.Has<T1>() || !x.Has<T2>()
|
||||
? x
|
||||
: lambda(x, x.Get<T1>(), x.Get<T2>()));
|
||||
}
|
||||
|
||||
public static EE ForEachEntity<T1, T2, T3>(
|
||||
this EE entities,
|
||||
Func<Entity, T1, T2, T3, Entity> lambda)
|
||||
{
|
||||
return entities
|
||||
.Select(
|
||||
x => !x.Has<T1>() || !x.Has<T2>() || !x.Has<T3>()
|
||||
? x
|
||||
: lambda(x, x.Get<T1>(), x.Get<T2>(), x.Get<T3>()));
|
||||
}
|
||||
|
||||
public static EE ForEntities<T1, T2>(
|
||||
this EE entities,
|
||||
Func<EE, EE> lambda)
|
||||
{
|
||||
List<Entity> list = entities.ToList();
|
||||
EE has = lambda(list.HasComponents<T1, T2>());
|
||||
EE hasNot = list.NotComponents<T1, T2>();
|
||||
|
||||
return hasNot.Union(has);
|
||||
}
|
||||
|
||||
public static EE ForEntities<T1, T2, T3>(
|
||||
this EE entities,
|
||||
Func<EE, EE> lambda)
|
||||
{
|
||||
List<Entity> list = entities.ToList();
|
||||
EE has = lambda(list.HasComponents<T1, T2, T3>());
|
||||
EE hasNot = list.NotComponents<T1, T2, T3>();
|
||||
|
||||
return hasNot.Union(has);
|
||||
}
|
||||
|
||||
public static EE ForEntities<T1, T2, T3, T4>(
|
||||
this EE entities,
|
||||
Func<EE, EE> lambda)
|
||||
{
|
||||
List<Entity> list = entities.ToList();
|
||||
EE has = lambda(list.HasComponents<T1, T2, T3, T4>());
|
||||
EE hasNot = list.NotComponents<T1, T2, T3, T4>();
|
||||
|
||||
return hasNot.Union(has);
|
||||
}
|
||||
|
||||
public static EE HasComponents<T1, T2>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => x.Has<T1>() && x.Has<T2>());
|
||||
}
|
||||
|
||||
public static EE HasComponents<T1, T2, T3>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => x.Has<T1>() && x.Has<T2>() && x.Has<T3>());
|
||||
}
|
||||
|
||||
public static EE HasComponents<T1, T2, T3, T4>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(
|
||||
x => x.Has<T1>()
|
||||
&& x.Has<T2>()
|
||||
&& x.Has<T3>()
|
||||
&& x.Has<T4>());
|
||||
}
|
||||
|
||||
public static EE NotComponents<T1, T2>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => !x.Has<T1>() || !x.Has<T2>());
|
||||
}
|
||||
|
||||
public static EE NotComponents<T1, T2, T3>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => !x.Has<T1>() || !x.Has<T2>() || !x.Has<T3>());
|
||||
}
|
||||
|
||||
public static EE NotComponents<T1, T2, T3, T4>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(
|
||||
x => !x.Has<T1>()
|
||||
|| !x.Has<T2>()
|
||||
|| !x.Has<T3>()
|
||||
|| !x.Has<T4>());
|
||||
}
|
||||
}
|
||||
}
|
68
src/Gallium/EnumerableEntityExtensions1.cs
Normal file
68
src/Gallium/EnumerableEntityExtensions1.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EE = System.Collections.Generic.IEnumerable<Gallium.Entity>;
|
||||
|
||||
namespace Gallium
|
||||
{
|
||||
public static class EnumerableEntityExtensions1
|
||||
{
|
||||
public static EE ForEachEntity<T1>(
|
||||
this EE entities,
|
||||
Func<Entity, T1, Entity> lambda)
|
||||
{
|
||||
return entities
|
||||
.Select(x => x.Has<T1>() ? lambda(x, x.Get<T1>()) : x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an inner set of operation for entities that have a specific
|
||||
/// component.
|
||||
/// </summary>
|
||||
/// <param name="entities">The input to scan.</param>
|
||||
/// <param name="lambda">
|
||||
/// The list operation for the ones that have those
|
||||
/// components.
|
||||
/// </param>
|
||||
/// <typeparam name="T1">The type of the first component.</typeparam>
|
||||
/// <returns>
|
||||
/// A combined list of components, with entities without the component
|
||||
/// coming first and the ones with the component coming last after being
|
||||
/// processed by the lambda.
|
||||
/// </returns>
|
||||
public static EE ForEntities<T1>(
|
||||
this EE entities,
|
||||
Func<EE, EE> lambda)
|
||||
{
|
||||
List<Entity> list = entities.ToList();
|
||||
EE has = lambda(list.HasComponents<T1>());
|
||||
EE hasNot = list.NotComponents<T1>();
|
||||
|
||||
return hasNot.Union(has);
|
||||
}
|
||||
|
||||
public static EE HasComponents<T1>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => x.Has<T1>());
|
||||
}
|
||||
|
||||
public static EE NotComponents<T1>(this EE entities)
|
||||
{
|
||||
return entities
|
||||
.Where(x => !x.Has<T1>());
|
||||
}
|
||||
|
||||
public static EE WhereEntities<T1>(
|
||||
this EE entities,
|
||||
Func<Entity, T1, bool> lambda,
|
||||
bool includeWithoutComponent = true)
|
||||
{
|
||||
return entities
|
||||
.Where(
|
||||
x => x.Has<T1>()
|
||||
? lambda(x, x.Get<T1>())
|
||||
: includeWithoutComponent);
|
||||
}
|
||||
}
|
||||
}
|
8
src/Gallium/Gallium.csproj
Normal file
8
src/Gallium/Gallium.csproj
Normal file
|
@ -0,0 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
38
src/Gallium/MergeEnumerableEntityExtensions.cs
Normal file
38
src/Gallium/MergeEnumerableEntityExtensions.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Gallium
|
||||
{
|
||||
public static class MergeEnumerableEntityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Merges two sets of entities using the identifier to determine which
|
||||
/// entities are the same. The `merge` function takes both of the
|
||||
/// entities with the Entity from the `input` first and the one from
|
||||
/// `other` second. The returning entity is put into the collection. If
|
||||
/// an entity from the input is not found in other, then it is just
|
||||
/// passed on.
|
||||
/// </summary>
|
||||
/// <param name="input">The enumerable of entities to merge to.</param>
|
||||
/// <param name="other">The collection of entities to merge from.</param>
|
||||
/// <param name="merge">The callback to merge the two.</param>
|
||||
/// <returns>An sequence of entities, merged and unmerged.</returns>
|
||||
public static IEnumerable<Entity> MergeEntities(
|
||||
this IEnumerable<Entity> input,
|
||||
ICollection<Entity> other,
|
||||
Func<Entity, Entity, Entity> merge)
|
||||
{
|
||||
return input
|
||||
.Select(
|
||||
entity =>
|
||||
{
|
||||
Entity? found = other.FirstOrDefault(y => y == entity);
|
||||
|
||||
return found == null
|
||||
? entity
|
||||
: merge(entity, found);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue