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