Compare commits
79 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5a7e7f6f2c | |
|
|
f4171fe60b | |
|
|
a834762450 | |
|
|
2a81e3f700 | |
|
|
95a68d3be5 | |
|
|
bdf02b7764 | |
|
|
b1e8c310c5 | |
|
|
fe373e2c3f | |
|
|
8a1e19dc11 | |
|
|
a77d5eb257 | |
|
|
f69d47a87e | |
|
|
616d9979e0 | |
|
|
1b8039809d | |
|
|
36c2df79e4 | |
|
|
77719f1bde | |
|
|
102e97cc9a | |
|
|
2aee784f51 | |
|
|
c56a9f7155 | |
|
|
240688a4e2 | |
|
|
713d0fa3b3 | |
|
|
e70179b362 | |
|
|
58377bcfbc | |
|
|
bcee9db3de | |
|
|
2403d96e90 | |
|
|
ece8921cfe | |
|
|
693acdc032 | |
|
|
ae23509c4a | |
|
|
bc16465ae0 | |
|
|
3115243f55 | |
|
|
759f562316 | |
|
|
fdf08687d6 | |
|
|
9708eba6cb | |
|
|
d4e779fc66 | |
|
|
e0ee97bd7f | |
|
|
0b3ba9241b | |
|
|
85ed3e2862 | |
|
|
3ce36faf2d | |
|
|
11f46f354d | |
|
|
2c1c5885ae | |
|
|
a5c98c4963 | |
|
|
7ad5a96f16 | |
|
|
88dc088cb7 | |
|
|
50e5256e7a | |
|
|
4fd8aac469 | |
|
|
7b077419ed | |
|
|
e7151356b8 | |
|
|
7c60425de0 | |
|
|
6f1ddd2bc4 | |
|
|
fd57037a6d | |
|
|
a993671120 | |
|
|
af32c8d0fd | |
|
|
2aaef3d111 | |
|
|
0161470415 | |
|
|
4a87967722 | |
|
|
1bec85a3dc | |
|
|
f2fbeedc89 | |
|
|
c9739f69ff | |
|
|
68daff2b6d | |
|
|
5f71f64626 | |
|
|
541e21d9c9 | |
|
|
ddd676514b | |
|
|
c997e07817 | |
|
|
498f5baf33 | |
|
|
e1f3078ba5 | |
|
|
c0dae11e23 | |
|
|
1737af9a71 | |
|
|
e3d416a3e2 | |
|
|
f8915f1080 | |
|
|
f642d81407 | |
|
|
0c6e33f634 | |
|
|
78f690c910 | |
|
|
09556e3786 | |
|
|
cfc92096b2 | |
|
|
d751359ea0 | |
|
|
0d66a30544 | |
|
|
788c6f75ea | |
|
|
c4d48202be | |
|
|
a8c0d94e44 | |
|
|
27bad0e740 |
|
|
@ -1,5 +1,6 @@
|
|||
ignore:
|
||||
- "Tests/"
|
||||
- "Benchmarks/"
|
||||
|
||||
comment:
|
||||
layout: header, changes, diff
|
||||
layout: header, changes, diff
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
github: [ctreffs]
|
||||
custom: ['https://www.paypal.com/donate?hosted_button_id=GCG3K54SKRALQ']
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
name: 🐛 Bug Report
|
||||
about: Something isn't working as expected, create a report to help us improve
|
||||
labels: bug
|
||||
---
|
||||
|
||||
<!--
|
||||
Thanks for contributing to this project!
|
||||
|
||||
Before you submit your issue, please replace each paragraph
|
||||
below with the relevant details for your bug, and complete
|
||||
the steps in the checklist by placing an 'x' in each box:
|
||||
|
||||
- [x] I've completed this task
|
||||
- [ ] This task isn't completed
|
||||
-->
|
||||
|
||||
### Bug Description
|
||||
|
||||
*A clear and concise description of what the bug is.
|
||||
Replace this paragraph with a short description of the incorrect behavior.
|
||||
(If this is a regression, please note the last version of the package that exhibited the correct behavior in addition to your current version.)*
|
||||
|
||||
### Information
|
||||
|
||||
- **Package version:** What tag or branch of this package are you using? e.g. tag `1.2.3` or branch `main`
|
||||
- **Platform version:** Please tell us the version number of your operating system. e.g. `macOS 11.2.3` or `Ubuntu 20.04`
|
||||
- **Swift version:** Paste the output of `swift --version` here.
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] If possible, I've reproduced the issue using the `main`/`master` branch of this package.
|
||||
- [ ] I've searched for existing issues under the issues tab.
|
||||
- [ ] The bug is reproducible
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
*Steps to reproduce the behavior:*
|
||||
|
||||
1. Go to '...'
|
||||
2. '....'
|
||||
|
||||
*Replace this paragraph with an explanation of how to reproduce the incorrect behavior.
|
||||
Include a simple code example, if possible.*
|
||||
|
||||
### Expected behavior
|
||||
|
||||
*A clear and concise description of what you expected to happen.
|
||||
Describe what you expect to happen.*
|
||||
|
||||
### Actual behavior
|
||||
|
||||
*Describe or copy/paste the behavior you observe.*
|
||||
|
||||
### Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
### Additional context
|
||||
|
||||
*Add any other context about the problem here.*
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
name: 💡 Feature Request
|
||||
about: A suggestion for a new feature or idea for this project
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
<!--
|
||||
Thanks for contributing to this project!
|
||||
|
||||
Before you submit your issue, please replace the paragraphs
|
||||
below with information about your proposed feature.
|
||||
-->
|
||||
|
||||
### Feature request
|
||||
|
||||
*Replace this paragraph with a description of your proposed feature.
|
||||
A clear and concise description of what the idea or problem is you want to solve.
|
||||
Please be sure to describe some concrete use cases for the new feature -- be as specific as possible.
|
||||
Provide links to existing issues or external references/discussions, if appropriate.*
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
*A clear and concise description of what you want to happen.*
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
*A clear and concise description of any alternative solutions or features you've considered.*
|
||||
|
||||
### Additional context
|
||||
|
||||
*Add any other context or screenshots about the feature request here.*
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord
|
||||
url: https://discord.gg/JMM7W6pCCc
|
||||
about: Questions or comments about using Fireblade? Ask here!
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
Thanks for contributing to this project!
|
||||
|
||||
Before you submit your request, please replace each paragraph
|
||||
below with the relevant details, and complete the steps in the
|
||||
checklist by placing an 'x' in each box:
|
||||
|
||||
- [x] I've completed this task
|
||||
- [ ] This task isn't completed
|
||||
-->
|
||||
|
||||
### Description
|
||||
|
||||
*Replace this paragraph with a description of your changes and rationale.
|
||||
Provide links to an existing issue or external references/discussions, if appropriate.*
|
||||
|
||||
### Detailed Design
|
||||
|
||||
*Include any additional information about the design here. At minimum, describe a synopsis of any public API additions.*
|
||||
|
||||
```swift
|
||||
/// The new feature implemented by this pull request.
|
||||
public struct Example: Collection {
|
||||
}
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
*How has the new feature been documented?
|
||||
Have the relevant portions of the guides in the Documentation folder been updated in addition to symbol-level documentation?*
|
||||
|
||||
### Testing
|
||||
|
||||
*How is the new feature tested?
|
||||
Please ensure CI is not broken*
|
||||
|
||||
### Performance
|
||||
|
||||
*How did you verify the new feature performs as expected?*
|
||||
|
||||
### Source Impact
|
||||
|
||||
*What is the impact of this change on existing users of this package? Does it deprecate or remove any existing API?*
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] I've read the [Contribution Guidelines](https://github.com/fireblade-engine/ecs/blob/master/CONTRIBUTING.md)
|
||||
- [ ] I've followed the coding style of the rest of the project.
|
||||
- [ ] I've added tests covering all new code paths my change adds to the project (to the extent possible).
|
||||
- [ ] I've added benchmarks covering new functionality (if appropriate).
|
||||
- [ ] I've verified that my change does not break any existing tests or introduce unexpected benchmark regressions.
|
||||
- [ ] I've updated the documentation (if appropriate).
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
swift: ["latest"]
|
||||
container:
|
||||
image: swift:${{ matrix.swift }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test
|
||||
run: swift test -c release --enable-xctest --parallel --xunit-output .build/xUnit-output.xml
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-artifacts-linux-${{ matrix.swift }}-${{ github.run_id }}
|
||||
path: |
|
||||
.build/*.yaml
|
||||
.build/*.xml
|
||||
.build/*.json
|
||||
.build/*.txt
|
||||
.build/**/*.xctest
|
||||
.build/**/*.json
|
||||
.build/**/*.txt
|
||||
if-no-files-found: warn
|
||||
include-hidden-files: true
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
CODECOV_XCODE_VERSION: "16.0" # Xcode version used to generate code coverage
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
config:
|
||||
- { os: "macos-14", xcode: "15.4" }
|
||||
- { os: "macos-15", xcode: "16.0" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Select Xcode ${{ matrix.config.xcode }}
|
||||
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.config.xcode }}.app
|
||||
|
||||
- name: Test
|
||||
run: swift test -c release --parallel --xunit-output .build/xUnit-output.xml --enable-code-coverage
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.config.xcode }}.app/Contents/Developer
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-artifacts-${{ matrix.config.xcode }}-${{ github.run_id }}
|
||||
path: |
|
||||
.build/*.yaml
|
||||
.build/*.xml
|
||||
.build/*.json
|
||||
.build/*.txt
|
||||
.build/**/*.xctest
|
||||
.build/**/*.json
|
||||
.build/**/*.txt
|
||||
if-no-files-found: warn
|
||||
include-hidden-files: true
|
||||
|
||||
# Only run coverage steps if the CODECOV_TOKEN is available and the matrix.xcode matches CODECOV_XCODE_VERSION
|
||||
- name: Generate coverage report
|
||||
if: env.CODECOV_TOKEN != '' && matrix.config.xcode == env.CODECOV_XCODE_VERSION
|
||||
run: xcrun llvm-cov export -format="lcov" .build/**/*PackageTests.xctest/Contents/MacOS/*PackageTests -instr-profile .build/**/codecov/default.profdata > coverage.lcov
|
||||
|
||||
- name: Upload code coverage report
|
||||
if: env.CODECOV_TOKEN != '' && matrix.config.xcode == env.CODECOV_XCODE_VERSION
|
||||
uses: codecov/codecov-action@v5.4.0
|
||||
with:
|
||||
token: ${{ env.CODECOV_TOKEN }}
|
||||
files: coverage.lcov
|
||||
fail_ci_if_error: true
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
name: WASM
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: ubuntu-latest
|
||||
container: swift:6.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: swiftwasm/setup-swiftwasm@v2
|
||||
- run: swift build --swift-sdk wasm32-unknown-wasi
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
defaults:
|
||||
run: # Use powershell because bash is not supported: https://github.com/compnerd/gha-setup-swift/issues/18#issuecomment-1705524890
|
||||
shell: pwsh
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-2019 # Windows SDK lower than 10.0.26100 is needed until https://github.com/swiftlang/swift/pull/79751 released!
|
||||
steps:
|
||||
|
||||
- name: Setup VS Dev Environment
|
||||
uses: seanmiddleditch/gha-setup-vsdevenv@v5
|
||||
|
||||
- name: Setup
|
||||
uses: compnerd/gha-setup-swift@v0.3.0
|
||||
with:
|
||||
branch: swift-5.10-release
|
||||
tag: 5.10-RELEASE
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test
|
||||
run: swift test -c release --parallel --xunit-output .build/xUnit-output.xml
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-artifacts-windows-${{ github.run_id }}
|
||||
path: |
|
||||
.build/*.yaml
|
||||
.build/*.xml
|
||||
.build/*.json
|
||||
.build/*.txt
|
||||
.build/**/*.xctest
|
||||
.build/**/*.json
|
||||
.build/**/*.txt
|
||||
if-no-files-found: warn
|
||||
include-hidden-files: true
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
macOS:
|
||||
runs-on: macOS-latest
|
||||
strategy:
|
||||
matrix:
|
||||
xcode: ["11.7", "12"]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: Select Xcode ${{ matrix.xcode }}
|
||||
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
|
||||
- name: Swift version
|
||||
run: swift --version
|
||||
- name: Test
|
||||
run: swift test -v --skip-update --parallel --enable-test-discovery --enable-code-coverage
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
- name: Generate coverage report
|
||||
run: xcrun llvm-cov export -format="lcov" .build/debug/*PackageTests.xctest/Contents/MacOS/*PackageTests -instr-profile .build/debug/codecov/default.profdata > coverage.lcov
|
||||
- name: Upload code coverage report
|
||||
uses: codecov/codecov-action@master
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
file: coverage.lcov
|
||||
fail_ci_if_error: true
|
||||
- name: Build Release
|
||||
run: swift build -c release
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
swift: ["5.1", "latest"]
|
||||
container:
|
||||
image: swift:${{ matrix.swift }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: "Update APT"
|
||||
shell: bash
|
||||
run: "apt update"
|
||||
- name: "Install curl"
|
||||
shell: bash
|
||||
run: "apt-get install -y curl"
|
||||
- name: Swift version
|
||||
run: swift --version
|
||||
- name: Test
|
||||
run: swift test -v --skip-update --parallel --enable-test-discovery --enable-code-coverage
|
||||
- name: Generate coverage report
|
||||
run: llvm-cov export -format="lcov" .build/debug/*PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > coverage.lcov
|
||||
- name: Upload code coverage report
|
||||
uses: codecov/codecov-action@master
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
file: coverage.lcov
|
||||
fail_ci_if_error: true
|
||||
- name: Build Release
|
||||
run: swift build -c release
|
||||
|
||||
|
||||
webAssembly:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: Swift version
|
||||
run: swift --version
|
||||
- name: Build
|
||||
uses: swiftwasm/swiftwasm-action@master
|
||||
with:
|
||||
shell-action: swift build --triple wasm32-unknown-wasi
|
||||
|
|
@ -1,32 +1,76 @@
|
|||
# Build and deploy DocC to GitHub pages. Based off of pointfreeco/swift-composable-architecture:
|
||||
# https://github.com/pointfreeco/swift-composable-architecture/blob/main/.github/workflows/documentation.yml
|
||||
name: Documentation
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- .github/workflows/documentation.yml
|
||||
- Sources/FirebladeECS/**.swift
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- .github/workflows/documentation.yml
|
||||
- Sources/FirebladeECS/**.swift
|
||||
|
||||
concurrency:
|
||||
group: docs-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
documentation:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Generate documentation
|
||||
uses: SwiftDocOrg/swift-doc@master
|
||||
- name: Select Xcode 16.0
|
||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||
|
||||
- name: Checkout Package
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
inputs: "Sources/FirebladeECS"
|
||||
output: "Documentation"
|
||||
- name: Publish to wiki
|
||||
uses: SwiftDocOrg/github-wiki-publish-action@master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout gh-pages Branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: "Documentation"
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
|
||||
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
|
||||
ref: gh-pages
|
||||
path: docs
|
||||
|
||||
- name: Build documentation
|
||||
run: >
|
||||
rm -rf docs/.git;
|
||||
rm -rf docs/master;
|
||||
git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.*" | tail -n +6 | xargs -I {} rm -rf {};
|
||||
|
||||
for tag in $(echo "master"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.*" | head -6);
|
||||
do
|
||||
if [ -d "docs/$tag/data/documentation/firebladeecs" ]
|
||||
then
|
||||
echo "✅ Documentation for "$tag" already exists.";
|
||||
else
|
||||
echo "⏳ Generating documentation for FirebladeECS @ "$tag" release.";
|
||||
rm -rf "docs/$tag";
|
||||
|
||||
git checkout .;
|
||||
git checkout "$tag";
|
||||
|
||||
DOCC_JSON_PRETTYPRINT=YES \
|
||||
swift package \
|
||||
--allow-writing-to-directory docs/"$tag" \
|
||||
generate-documentation \
|
||||
--fallback-bundle-identifier com.github.fireblade-engine.FirebladeECS \
|
||||
--target FirebladeECS \
|
||||
--output-path docs/"$tag" \
|
||||
--transform-for-static-hosting \
|
||||
--hosting-base-path ecs/"$tag" \
|
||||
&& echo "✅ Documentation generated for FirebladeECS @ "$tag" release." \
|
||||
|| echo "⚠️ Documentation skipped for FirebladeECS @ "$tag".";
|
||||
fi;
|
||||
done
|
||||
|
||||
- name: Fix permissions
|
||||
run: 'sudo chown -R $USER docs'
|
||||
|
||||
- name: Publish documentation to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@v4.7.3
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: docs
|
||||
single-commit: true
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@v4
|
||||
- name: markdown-link-check
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@master
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@1.0.16
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
image: swift:5.1.3
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build_release
|
||||
|
||||
build_project:
|
||||
stage: build_release
|
||||
script:
|
||||
- swift package reset
|
||||
- swift build -c release
|
||||
tags:
|
||||
- docker
|
||||
only:
|
||||
- master
|
||||
|
||||
test_project:
|
||||
stage: test
|
||||
variables:
|
||||
GIT_DEPTH: "50"
|
||||
script:
|
||||
- swift package reset
|
||||
- swift test
|
||||
tags:
|
||||
- docker
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
version: 1
|
||||
builder:
|
||||
configs:
|
||||
- documentation_targets: [FirebladeECS]
|
||||
|
|
@ -1 +1 @@
|
|||
5.1.3
|
||||
5.8
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# file options
|
||||
--exclude Sources/**/*.generated.swift
|
||||
--exclude Sources/FirebladeECS/Entity+Component.swift # problems with self.get { }
|
||||
--exclude Tests/**/*.swift
|
||||
|
||||
# format options
|
||||
--extensionacl on-declarations
|
||||
--stripunusedargs closure-only
|
||||
--commas inline
|
||||
--self remove
|
||||
--selfrequired get
|
||||
--disable preferKeyPath
|
||||
--disable opaqueGenericParameters
|
||||
|
|
@ -9,23 +9,11 @@ identifier_name:
|
|||
line_length: 220
|
||||
number_separator:
|
||||
minimum_length: 5
|
||||
analyzer_rules:
|
||||
- explicit_self
|
||||
- unused_declaration
|
||||
- unused_import
|
||||
opt_in_rules:
|
||||
#- explicit_acl
|
||||
#- explicit_enum_raw_value
|
||||
#- explicit_type_interface
|
||||
#- extension_access_modifier
|
||||
#- file_name
|
||||
#- file_types_order
|
||||
#- indentation_width
|
||||
#- missing_docs
|
||||
#- multiline_arguments_brackets
|
||||
#- multiline_literal_brackets
|
||||
#- multiline_parameters_brackets
|
||||
#- no_grouping_extension
|
||||
#- required_deinit
|
||||
#- type_contents_order
|
||||
#- unowned_variable_capture
|
||||
- anyobject_protocol
|
||||
- array_init
|
||||
- attributes
|
||||
- closure_body_length
|
||||
|
|
@ -48,7 +36,6 @@ opt_in_rules:
|
|||
- enum_case_associated_values_count
|
||||
- expiring_todo
|
||||
- explicit_init
|
||||
- explicit_self
|
||||
- explicit_top_level_acl
|
||||
- fallthrough
|
||||
- fatal_error_message
|
||||
|
|
@ -109,8 +96,6 @@ opt_in_rules:
|
|||
- unavailable_function
|
||||
- unneeded_parentheses_in_closure_argument
|
||||
- untyped_error_in_catch
|
||||
- unused_declaration
|
||||
- unused_import
|
||||
- vertical_parameter_alignment_on_call
|
||||
- vertical_whitespace_between_cases
|
||||
- vertical_whitespace_closing_braces
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// Base.swift
|
||||
// FirebladeECSTests
|
||||
//
|
||||
// Created by Christian Treffs on 09.10.17.
|
||||
//
|
||||
|
||||
import FirebladeECS
|
||||
|
||||
class EmptyComponent: Component {}
|
||||
|
||||
class Name: Component {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
class Position: Component {
|
||||
var x: Int
|
||||
var y: Int
|
||||
init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
}
|
||||
|
||||
class Velocity: Component {
|
||||
var a: Float
|
||||
init(a: Float) {
|
||||
self.a = a
|
||||
}
|
||||
}
|
||||
|
||||
class Party: Component {
|
||||
var partying: Bool
|
||||
init(partying: Bool) {
|
||||
self.partying = partying
|
||||
}
|
||||
}
|
||||
|
||||
class Color: Component {
|
||||
var r: UInt8 = 0
|
||||
var g: UInt8 = 0
|
||||
var b: UInt8 = 0
|
||||
}
|
||||
|
||||
class ExampleSystem {
|
||||
private let family: Family2<Position, Velocity>
|
||||
|
||||
init(nexus: Nexus) {
|
||||
family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self)
|
||||
}
|
||||
|
||||
func update(deltaT _: Double) {
|
||||
for (position, velocity) in family {
|
||||
position.x *= 2
|
||||
velocity.a *= 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class SingleGameState: SingleComponent {
|
||||
var shouldQuit: Bool = false
|
||||
var playerHealth: Int = 67
|
||||
}
|
||||
|
||||
func setUpNexus() -> Nexus {
|
||||
let numEntities = 10000
|
||||
let nexus = Nexus()
|
||||
|
||||
for i in 0 ..< numEntities {
|
||||
nexus.createEntity().assign(Position(x: 1 + i, y: 2 + i),
|
||||
Name(name: "myName\(i)"),
|
||||
Velocity(a: 3.14),
|
||||
EmptyComponent(),
|
||||
Color())
|
||||
}
|
||||
|
||||
precondition(nexus.numEntities == numEntities)
|
||||
// precondition(nexus.numFamilies == 1)
|
||||
precondition(nexus.numComponents == numEntities * 5)
|
||||
|
||||
return nexus
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
// swiftformat:disable preferForLoop
|
||||
import Benchmark
|
||||
import FirebladeECS
|
||||
|
||||
// derived from FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift in the parent project
|
||||
|
||||
let benchmarks = {
|
||||
Benchmark("TraitMatching") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let a = nexus.createEntity()
|
||||
a.assign(Position(x: 1, y: 2))
|
||||
a.assign(Name(name: "myName"))
|
||||
a.assign(Velocity(a: 3.14))
|
||||
a.assign(EmptyComponent())
|
||||
|
||||
let isMatch = nexus.family(requiresAll: Position.self, Velocity.self,
|
||||
excludesAll: Party.self)
|
||||
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
isMatch.canBecomeMember(a)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyEntities") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.entities
|
||||
.forEach { (entity: Entity) in
|
||||
_ = entity
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyOneComponent") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.forEach { (position: Position) in
|
||||
_ = position
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyEntityOneComponent") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requires: Position.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position) in
|
||||
_ = entity
|
||||
_ = position
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyTwoComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Benchmark("TypedFamilyEntityTwoComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position, velocity: Velocity) in
|
||||
_ = entity
|
||||
_ = position
|
||||
_ = velocity
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyThreeComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity, name: Name) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Benchmark("TypedFamilyEntityThreeComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name) in
|
||||
_ = entity
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyFourComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity, name: Name, color: Color) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyEntityFourComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self)
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color) in
|
||||
_ = entity
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyFiveComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(
|
||||
family
|
||||
.forEach { (position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
_ = empty
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark("TypedFamilyEntityFiveComponents") { benchmark in
|
||||
let nexus = setUpNexus()
|
||||
let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self)
|
||||
|
||||
for _ in benchmark.scaledIterations {
|
||||
blackHole(family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in
|
||||
_ = entity
|
||||
_ = position
|
||||
_ = velocity
|
||||
_ = name
|
||||
_ = color
|
||||
_ = empty
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// swift-tools-version: 5.8
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "ECSBenchmarks",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
.macOS(.v13)
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../"),
|
||||
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.29.2"))
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "ECSBenchmark",
|
||||
dependencies: [
|
||||
.product(name: "FirebladeECS", package: "ecs"),
|
||||
.product(name: "Benchmark", package: "package-benchmark")
|
||||
],
|
||||
path: "Benchmarks/ECSBenchmark",
|
||||
plugins: [
|
||||
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Benchmarks for FirebladeECS
|
||||
|
||||
Originally seeded by replicating performance tests into a new form leveraging [package-benchmark](https://swiftpackageindex.com/ordo-one/package-benchmark/) [Documentation](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark).
|
||||
|
||||
To run all the available benchmarks:
|
||||
|
||||
swift package benchmark --format markdown
|
||||
|
||||
For more help on the package-benchmark SwiftPM plugin:
|
||||
|
||||
swift package benchmark help
|
||||
|
||||
Creating a local baseline:
|
||||
|
||||
swift package --allow-writing-to-package-directory benchmark baseline update dev
|
||||
swift package benchmark baseline list
|
||||
|
||||
Comparing to a the baseline 'alpha'
|
||||
|
||||
swift package benchmark baseline compare dev
|
||||
|
||||
For more details on creating and comparing baselines, read [Creating and Comparing Benchmark Baselines](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark/creatingandcomparingbaselines).
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# This file is a list of the people responsible for ensuring that contributions
|
||||
# to this projected are reviewed, either by themselves or by someone else.
|
||||
# They are also the gatekeepers for their part of this project, with the final
|
||||
# word on what goes in or not.
|
||||
# The code owners file uses a .gitignore-like syntax to specify which parts of
|
||||
# the codebase is associated with an owner. See
|
||||
# <https://docs.github.com/github/creating-cloning-and-archiving-repositories/about-code-owners>
|
||||
# for details.
|
||||
# The following lines are used by GitHub to automatically recommend reviewers.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
* @ctreffs
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement
|
||||
e.g. via [content abuse report][ref-report-abuse].
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][ref-homepage-cc],
|
||||
version 2.0, available at
|
||||
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
<https://www.contributor-covenant.org/faq>.
|
||||
Translations are available at
|
||||
<https://www.contributor-covenant.org/translations>.
|
||||
|
||||
<!-- REFERENCES -->
|
||||
|
||||
[ref-homepage-cc]: https://www.contributor-covenant.org
|
||||
[ref-report-abuse]: https://docs.github.com/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam#reporting-an-issue-or-pull-request
|
||||
[ref-gh-coc]: https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project
|
||||
[ref-gh-abuse]: https://docs.github.com/en/communities/moderating-comments-and-conversations/managing-how-contributors-report-abuse-in-your-organizations-repository
|
||||
[ref-coc-guide]: https://opensource.guide/code-of-conduct/
|
||||
|
||||
|
|
@ -8,18 +8,18 @@
|
|||
The following is a set of **guidelines for contributing** to this project.
|
||||
Use your best judgment and feel free to propose changes to this document in a pull request.
|
||||
|
||||
**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)
|
||||
**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
|
||||
|
||||
### 💡 Your contribution - the sky is the limit 🌈
|
||||
|
||||
This is an open source project and we love to receive contributions from our community — [**you**][ref-contributors]!
|
||||
|
||||
There are many ways to contribute, from writing __tutorials__ or __blog posts__, improving the [__documentation__][ref-documentation], submitting __bug reports__ and __feature requests__ or
|
||||
__writing code__ which can be incorporated into the repository itself.
|
||||
There are many ways to contribute, from writing __tutorials__ or __blog posts__, improving the [__documentation__][ref-documentation], submitting [__bug reports__][ref-issues-new] and [__enhancement__][ref-pull-request-new] or
|
||||
[__writing code__][ref-pull-request-new] which can be incorporated into the repository itself.
|
||||
|
||||
When contributing to this project, please feel free to discuss the change you wish to make via issue with the repository owners before making a change.
|
||||
When contributing to this project, please feel free to discuss the changes and ideas you wish to contribute with the repository owners before making a change by opening a [new issue][ref-issues-new] and add the **feature request** tag to that issue.
|
||||
|
||||
<!--Note that we have a [code of conduct][ref-code-of-conduct], please follow it in all your interactions with the project.-->
|
||||
Note that we have a [code of conduct][ref-code-of-conduct], please follow it in all your interactions with the project.
|
||||
|
||||
### 🐞 You want to report a bug or file an issue?
|
||||
|
||||
|
|
@ -55,11 +55,12 @@ If you want to start somewhere, this would be a good place to start.
|
|||
That said, these aren't necessarily the easiest tickets.
|
||||
|
||||
For any new contributions please consider these guidelines:
|
||||
|
||||
1. Open a [new pull request (PR)][ref-pull-request-new] with a **clear and descriptive title**
|
||||
2. Write a **detailed comment** with as much relevant information as possible including:
|
||||
- What your feature is intended to do?
|
||||
- How it can be used?
|
||||
- What alternatives where considered?
|
||||
- What alternatives where considered, if any?
|
||||
- Has this feature impact on performance or stability of the project?
|
||||
|
||||
#### Your contribution responsibilities
|
||||
|
|
@ -68,7 +69,7 @@ Don't be intimidated by these responsibilities, they are easy to meet if you tak
|
|||
|
||||
- [x] Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback.
|
||||
- [x] Ensure (cross-)platform compatibility for every change that's accepted. An addition should not reduce the number of platforms that the project supports.
|
||||
- [x] Ensure **coding conventions** are met. Lint your code with the project's default tools.
|
||||
- [x] Ensure **coding conventions** are met. Lint your code with the project's default tools. Project wide commands are available through the [Makefile][ref-makefile] in the repository root.
|
||||
- [x] Add tests for your feature that prove it's working as expected. Code coverage should not drop below its previous value.
|
||||
- [x] Ensure none of the existing tests are failing after adding your changes.
|
||||
- [x] Document your public API code and ensure to add code comments where necessary.
|
||||
|
|
@ -80,7 +81,7 @@ Please consult the [README][ref-readme] for installation instructions.
|
|||
|
||||
<!-- REFERENCES -->
|
||||
|
||||
[ref-code-of-conduct]: t.b.d.
|
||||
[ref-code-of-conduct]: https://github.com/fireblade-engine/ecs/blob/master/CODE_OF_CONDUCT.md
|
||||
[ref-contributors]: https://github.com/fireblade-engine/ecs/graphs/contributors
|
||||
[ref-documentation]: https://github.com/fireblade-engine/ecs/wiki
|
||||
[ref-gh-actions]: https://github.com/fireblade-engine/ecs/actions
|
||||
|
|
@ -91,3 +92,4 @@ Please consult the [README][ref-readme] for installation instructions.
|
|||
[ref-pull-request-how-to]: https://docs.github.com/en/github/writing-on-github/autolinked-references-and-urls
|
||||
[ref-pull-request-new]: https://github.com/fireblade-engine/ecs/compare
|
||||
[ref-readme]: https://github.com/fireblade-engine/ecs/blob/master/README.md
|
||||
[ref-makefile]: https://github.com/fireblade-engine/ecs/blob/master/Makefile
|
||||
|
|
|
|||
132
Makefile
132
Makefile
|
|
@ -1,75 +1,95 @@
|
|||
# Version 1.0.0
|
||||
UNAME_S := $(shell uname -s)
|
||||
SWIFT_PACKAGE_VERSION := $(shell swift package tools-version)
|
||||
|
||||
# Lint
|
||||
lint:
|
||||
swiftlint autocorrect --format
|
||||
swiftlint lint --quiet
|
||||
# Lint fix and format code.
|
||||
.PHONY: lint-fix
|
||||
swiftlint:
|
||||
mint run swiftlint lint --fix --config .swiftlint.yml --format --quiet
|
||||
swiftformat:
|
||||
mint run swiftformat . --swiftversion ${SWIFT_PACKAGE_VERSION}
|
||||
lint-fix: swiftlint swiftformat
|
||||
|
||||
lintErrorOnly:
|
||||
@swiftlint autocorrect --format --quiet
|
||||
@swiftlint lint --quiet | grep error
|
||||
# Generate code
|
||||
.PHONY: generate-code
|
||||
generate-code:
|
||||
mint run sourcery --quiet --config ./.sourcery.yml
|
||||
mint run sourcery --quiet --config ./.sourceryTests.yml
|
||||
|
||||
# Git
|
||||
precommit: generateCode generateTestsCode lint genLinuxTests
|
||||
# Run pre-push tasks
|
||||
.PHONY: pre-push
|
||||
pre-push: generate-code lint-fix
|
||||
|
||||
submodule:
|
||||
git submodule init
|
||||
git submodule update --recursive
|
||||
.PHONY: precommit
|
||||
precommit: pre-push
|
||||
|
||||
# Tests
|
||||
genLinuxTests:
|
||||
swift test --generate-linuxmain
|
||||
swiftlint autocorrect --format --path Tests/
|
||||
.PHONY: setup-brew
|
||||
setup-brew:
|
||||
@which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
|
||||
@brew update
|
||||
|
||||
test: genLinuxTests
|
||||
swift test
|
||||
.PHONY: install-dependencies-macOS
|
||||
install-dependencies-macOS: setup-brew
|
||||
brew install mint
|
||||
mint bootstrap
|
||||
|
||||
# Package
|
||||
latest:
|
||||
swift package update
|
||||
.PHONY: setupEnvironment
|
||||
setupEnvironment: install-dependencies-macOS
|
||||
|
||||
resolve:
|
||||
swift package resolve
|
||||
# Build debug version
|
||||
.PHONY: build-debug
|
||||
build-debug:
|
||||
swift build -c debug
|
||||
|
||||
# Xcode
|
||||
genXcode:
|
||||
swift package generate-xcodeproj --enable-code-coverage --skip-extra-files
|
||||
|
||||
genXcodeOpen: genXcode
|
||||
open *.xcodeproj
|
||||
|
||||
# Clean
|
||||
clean:
|
||||
swift package reset
|
||||
rm -rdf .swiftpm/xcode
|
||||
rm -rdf .build/
|
||||
rm Package.resolved
|
||||
rm .DS_Store
|
||||
|
||||
cleanArtifacts:
|
||||
swift package clean
|
||||
# Build release version
|
||||
.PHONY: build-release
|
||||
build-release:
|
||||
swift build -c release --skip-update
|
||||
|
||||
# Test links in README
|
||||
# requires <https://github.com/tcort/markdown-link-check>
|
||||
.PHONY: testReadme
|
||||
testReadme:
|
||||
markdown-link-check -p -v ./README.md
|
||||
|
||||
generateCode:
|
||||
sourcery --config ./.sourcery.yml --verbose
|
||||
generateTestsCode:
|
||||
sourcery --config ./.sourceryTests.yml --verbose
|
||||
# Delete package build artifacts.
|
||||
.PHONY: clean
|
||||
clean: clean-sourcery
|
||||
swift package clean
|
||||
|
||||
brewInstallDeps: brewUpdate
|
||||
brew install swiftenv
|
||||
brew install swiftlint
|
||||
brew install sourcery
|
||||
# Clean sourcery cache
|
||||
.PHONY: clean-sourcery
|
||||
clean-sourcery:
|
||||
rm -rdf ${HOME}/Library/Caches/Sourcery
|
||||
|
||||
brewSetup:
|
||||
which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
|
||||
# Preview DocC documentation
|
||||
.PHONY: preview-docs
|
||||
preview-docs:
|
||||
swift package --disable-sandbox preview-documentation --target FirebladeECS
|
||||
|
||||
brewUpdate: brewSetup
|
||||
brew update
|
||||
# Preview DocC documentation with analysis/warnings and overview of coverage
|
||||
.PHONY: preview-analysis-docs
|
||||
preview-analysis-docs:
|
||||
swift package --disable-sandbox preview-documentation --target FirebladeECS --analyze --experimental-documentation-coverage --level brief
|
||||
|
||||
setupEnvironment: brewInstallDeps
|
||||
open Package.swift
|
||||
# Generates a plain DocC archive in the .build directory
|
||||
.PHONY: generate-docs
|
||||
generate-docs:
|
||||
DOCC_JSON_PRETTYPRINT=YES \
|
||||
swift package \
|
||||
generate-documentation \
|
||||
--fallback-bundle-identifier com.github.fireblade-engine.FirebladeECS \
|
||||
--target FirebladeECS \
|
||||
|
||||
# Generates documentation pages suitable to push/host on github pages (or another static site)
|
||||
# Expected location, if set up, would be:
|
||||
# https://fireblade-engine.github.io/FirebladeECS/documentation/FirebladeECS/
|
||||
.PHONY: generate-docs-githubpages
|
||||
generate-docs-githubpages:
|
||||
DOCC_JSON_PRETTYPRINT=YES \
|
||||
swift package \
|
||||
--allow-writing-to-directory ./docs \
|
||||
generate-documentation \
|
||||
--fallback-bundle-identifier com.github.fireblade-engine.FirebladeECS \
|
||||
--target FirebladeECS \
|
||||
--output-path ./docs \
|
||||
--transform-for-static-hosting \
|
||||
--hosting-base-path 'FirebladeECS'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
realm/SwiftLint@0.57.0
|
||||
nicklockwood/SwiftFormat@0.55.0
|
||||
krzysztofzablocki/Sourcery@2.2.5
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-docc-plugin",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-plugin",
|
||||
"state" : {
|
||||
"revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64",
|
||||
"version" : "1.4.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-symbolkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
|
||||
"state" : {
|
||||
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
|
||||
"version" : "1.0.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// swift-tools-version:5.1
|
||||
// swift-tools-version:5.8
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
|
|
@ -7,10 +7,15 @@ let package = Package(
|
|||
.library(name: "FirebladeECS",
|
||||
targets: ["FirebladeECS"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.3")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "FirebladeECS"),
|
||||
.target(name: "FirebladeECS",
|
||||
exclude: ["Stencils/Family.stencil"]),
|
||||
.testTarget(name: "FirebladeECSTests",
|
||||
dependencies: ["FirebladeECS"]),
|
||||
dependencies: ["FirebladeECS"],
|
||||
exclude: ["Stencils/FamilyTests.stencil"]),
|
||||
.testTarget(name: "FirebladeECSPerformanceTests",
|
||||
dependencies: ["FirebladeECS"])
|
||||
],
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -1,11 +1,13 @@
|
|||
# Fireblade ECS (Entity-Component System)
|
||||
[](LICENSE)
|
||||
[](https://github.com/fireblade-engine/ecs/actions?query=workflow%3ACI)
|
||||
[](https://codecov.io/gh/fireblade-engine/ecs)
|
||||
[](https://github.com/fireblade-engine/ecs/actions/workflows/ci-macos.yml)
|
||||
[](https://github.com/fireblade-engine/ecs/actions/workflows/ci-linux.yml)
|
||||
[](https://github.com/fireblade-engine/ecs/actions/workflows/ci-windows.yml)
|
||||
[](https://github.com/fireblade-engine/ecs/actions/workflows/ci-wasm.yml)
|
||||
[](https://github.com/fireblade-engine/ecs/wiki)
|
||||
[](https://codecov.io/gh/fireblade-engine/ecs)
|
||||
[](https://swiftpackageindex.com/fireblade-engine/ecs)
|
||||
[](https://swiftpackageindex.com/fireblade-engine/ecs)
|
||||
[](https://github.com/swiftwasm/swift#swiftwasm)
|
||||
|
||||
This is a **dependency free**, **lightweight**, **fast** and **easy to use** [Entity-Component System](https://en.wikipedia.org/wiki/Entity_component_system) implementation in Swift. It is developed and maintained as part of the [Fireblade Game Engine project](https://github.com/fireblade-engine).
|
||||
|
||||
|
|
@ -23,19 +25,19 @@ These instructions will get you a copy of the project up and running on your loc
|
|||
|
||||
### 💻 Installing
|
||||
|
||||
Fireblade ECS is available for all platforms that support [Swift 5.1](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
||||
Fireblade ECS is available for all platforms that support [Swift 5.8](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
||||
|
||||
Extend the following lines in your `Package.swift` file or use it to create a new project.
|
||||
|
||||
```swift
|
||||
// swift-tools-version:5.1
|
||||
// swift-tools-version:5.8
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourPackageName",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.2")
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.5")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
@ -231,8 +233,9 @@ See the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo) t
|
|||
|
||||
## 📖 Documentation
|
||||
|
||||
Consult the [wiki](https://github.com/fireblade-engine/ecs/wiki) for in-depth [documentation](https://github.com/fireblade-engine/ecs/wiki).
|
||||
Consult the [online documentation](https://swiftpackageindex.com/fireblade-engine/ecs/documentation/FirebladeECS), or preview it locally:
|
||||
|
||||
- `make preview-docs`
|
||||
|
||||
## 💁 How to contribute
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ public struct DynamicCodingKey: CodingKey {
|
|||
public var intValue: Int?
|
||||
public var stringValue: String
|
||||
|
||||
public init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
|
||||
public init?(intValue: Int) { self.intValue = intValue; stringValue = "\(intValue)" }
|
||||
public init?(stringValue: String) { self.stringValue = stringValue }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ public struct ComponentIdentifier {
|
|||
|
||||
extension ComponentIdentifier {
|
||||
@usableFromInline
|
||||
init<C>(_ componentType: C.Type) where C: Component {
|
||||
self.id = Self.makeRuntimeHash(componentType)
|
||||
init(_ componentType: (some Component).Type) {
|
||||
id = Self.makeRuntimeHash(componentType)
|
||||
}
|
||||
|
||||
/// object identifier hash (only stable during runtime) - arbitrary hash is ok.
|
||||
internal static func makeRuntimeHash<C>(_ componentType: C.Type) -> Identifier where C: Component {
|
||||
static func makeRuntimeHash(_ componentType: (some Component).Type) -> Identifier {
|
||||
ObjectIdentifier(componentType).hashValue
|
||||
}
|
||||
}
|
||||
|
||||
extension ComponentIdentifier: Equatable { }
|
||||
extension ComponentIdentifier: Hashable { }
|
||||
extension ComponentIdentifier: Equatable {}
|
||||
extension ComponentIdentifier: Hashable {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
# ``FirebladeECS``
|
||||
|
||||
Seamlessly, consistently, and asynchronously replicate data.
|
||||
|
||||
## Overview
|
||||
|
||||
This is a **dependency free**, **lightweight**, **fast** and **easy to use** [Entity-Component System](https://en.wikipedia.org/wiki/Entity_component_system) implementation in Swift.
|
||||
An ECS comprises entities composed from components of data, with systems which operate on the components.
|
||||
|
||||
Fireblade ECS is available for all platforms that support [Swift 5.8](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
||||
It is developed and maintained as part of the [Fireblade Game Engine project](https://github.com/fireblade-engine).
|
||||
|
||||
For a more detailed example of FirebladeECS in action, see the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo).
|
||||
|
||||
## Topics
|
||||
|
||||
### Essentials
|
||||
|
||||
- <doc:GettingStartedWithFirebladeECS>
|
||||
- ``Nexus``
|
||||
- ``NexusEvent``
|
||||
- ``NexusEventDelegate``
|
||||
|
||||
### Entities
|
||||
|
||||
- ``Entity``
|
||||
- ``EntityState``
|
||||
- ``EntityStateMachine``
|
||||
- ``EntityCreated``
|
||||
- ``EntityDestroyed``
|
||||
- ``EntityComponentHash``
|
||||
- ``EntityIdentifier``
|
||||
- ``EntityIdentifierGenerator``
|
||||
- ``DefaultEntityIdGenerator``
|
||||
- ``LinearIncrementingEntityIdGenerator``
|
||||
|
||||
### Components
|
||||
|
||||
- ``Component``
|
||||
- ``ComponentAdded``
|
||||
- ``ComponentRemoved``
|
||||
- ``ComponentProvider``
|
||||
- ``ComponentsBuilder-4co42``
|
||||
- ``ComponentsBuilder``
|
||||
- ``ComponentInstanceProvider``
|
||||
- ``ComponentIdentifier``
|
||||
- ``ComponentInitializable``
|
||||
- ``ComponentTypeHash``
|
||||
- ``ComponentTypeProvider``
|
||||
- ``ComponentSingletonProvider``
|
||||
- ``SingleComponent``
|
||||
- ``EntityComponentHash``
|
||||
- ``StateComponentMapping``
|
||||
- ``DynamicComponentProvider``
|
||||
- ``RequiringComponents1``
|
||||
- ``RequiringComponents2``
|
||||
- ``RequiringComponents3``
|
||||
- ``RequiringComponents4``
|
||||
- ``RequiringComponents5``
|
||||
- ``RequiringComponents6``
|
||||
- ``RequiringComponents7``
|
||||
- ``RequiringComponents8``
|
||||
- ``DefaultInitializable``
|
||||
- ``SingleComponent``
|
||||
|
||||
### Systems
|
||||
|
||||
- ``Family``
|
||||
- ``FamilyEncoding``
|
||||
- ``FamilyDecoding``
|
||||
- ``FamilyMemberAdded``
|
||||
- ``FamilyMemberRemoved``
|
||||
- ``FamilyMemberBuilder-3f2i6``
|
||||
- ``FamilyMemberBuilder``
|
||||
- ``FamilyTraitSet``
|
||||
- ``Requires1``
|
||||
- ``Requires2``
|
||||
- ``Requires3``
|
||||
- ``Requires4``
|
||||
- ``Requires5``
|
||||
- ``Requires6``
|
||||
- ``Requires7``
|
||||
- ``Requires8``
|
||||
- ``Single``
|
||||
- ``Family1``
|
||||
- ``Family2``
|
||||
- ``Family3``
|
||||
- ``Family4``
|
||||
- ``Family5``
|
||||
- ``Family6``
|
||||
- ``Family7``
|
||||
- ``Family8``
|
||||
- ``FamilyRequirementsManaging``
|
||||
|
||||
### Coding Strategies
|
||||
|
||||
- ``CodingStrategy``
|
||||
- ``DefaultCodingStrategy``
|
||||
- ``TopLevelDecoder``
|
||||
- ``TopLevelEncoder``
|
||||
- ``DynamicCodingKey``
|
||||
|
||||
### Supporting Types
|
||||
|
||||
- ``ManagedContiguousArray``
|
||||
- ``UnorderedSparseSet``
|
||||
|
||||
### Hash Functions
|
||||
|
||||
- ``hash(combine:)``
|
||||
- ``hash(combine:_:)``
|
||||
- ``StringHashing``
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
# Getting started with Fireblade ECS
|
||||
|
||||
Learn the API and key types Fireblade provides to compose your game or app logic.
|
||||
|
||||
## Overview
|
||||
|
||||
Fireblade ECS is a dependency free, Swift language implementation of an Entity-Component-System ([ECS](https://en.wikipedia.org/wiki/Entity_component_system)).
|
||||
An ECS comprises entities composed from components of data, with systems which operate on the components.
|
||||
|
||||
Extend the following lines in your `Package.swift` file or use it to create a new project.
|
||||
|
||||
```swift
|
||||
// swift-tools-version:5.8
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourPackageName",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.5")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "YourTargetName",
|
||||
dependencies: ["FirebladeECS"])
|
||||
]
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
This article introduces you to the key concepts of Fireblade ECS's API.
|
||||
For a more detailed example, see the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo).
|
||||
|
||||
### 🏛️ Nexus
|
||||
|
||||
The core element in the Fireblade-ECS is the [Nexus](https://en.wiktionary.org/wiki/nexus#Noun).
|
||||
It acts as a centralized way to store, access and manage entities and their components.
|
||||
A single `Nexus` may (theoretically) hold up to 4294967295 `Entities` at a time.
|
||||
You may use more than one `Nexus` at a time.
|
||||
|
||||
Initialize a `Nexus` with
|
||||
|
||||
```swift
|
||||
let nexus = Nexus()
|
||||
```
|
||||
|
||||
### 👤 Entities
|
||||
|
||||
then create entities by letting the `Nexus` generate them.
|
||||
|
||||
```swift
|
||||
// an entity without components
|
||||
let newEntity = nexus.createEntity()
|
||||
```
|
||||
|
||||
To define components, conform your class to the `Component` protocol
|
||||
|
||||
```swift
|
||||
final class Position: Component {
|
||||
var x: Int = 0
|
||||
var y: Int = 0
|
||||
}
|
||||
```
|
||||
and assign instances of it to an `Entity` with
|
||||
|
||||
```swift
|
||||
let position = Position(x: 1, y: 2)
|
||||
entity.assign(position)
|
||||
```
|
||||
|
||||
You can be more efficient by assigning components while creating an entity.
|
||||
|
||||
```swift
|
||||
// an entity with two components assigned.
|
||||
nexus.createEntity {
|
||||
Position(x: 1, y: 2)
|
||||
Color(.red)
|
||||
}
|
||||
|
||||
// bulk create entities with multiple components assigned.
|
||||
nexus.createEntities(count: 100) { _ in
|
||||
Position()
|
||||
Color()
|
||||
}
|
||||
|
||||
```
|
||||
### 👪 Families
|
||||
|
||||
This ECS uses a grouping approach for entities with the same component types to optimize cache locality and ease up access to them.
|
||||
Entities with the __same component types__ may belong to one `Family`.
|
||||
A `Family` has entities as members and component types as family traits.
|
||||
|
||||
Create a family by calling `.family` with a set of traits on the nexus.
|
||||
A family that contains only entities with a `Movement` and `PlayerInput` component, but no `Texture` component is created by
|
||||
|
||||
```swift
|
||||
let family = nexus.family(requiresAll: Movement.self, PlayerInput.self,
|
||||
excludesAll: Texture.self)
|
||||
```
|
||||
|
||||
These entities are cached in the nexus for efficient access and iteration.
|
||||
Families conform to the [Sequence](https://developer.apple.com/documentation/swift/sequence) protocol so that members (components)
|
||||
may be iterated and accessed like any other sequence in Swift.
|
||||
Access a family's components directly on the family instance. To get family entities and access components at the same time call `family.entityAndComponents`.
|
||||
If you are only interested in a family's entities call `family.entities`.
|
||||
|
||||
```swift
|
||||
class PlayerMovementSystem {
|
||||
let family = nexus.family(requiresAll: Movement.self, PlayerInput.self,
|
||||
excludesAll: Texture.self)
|
||||
|
||||
func update() {
|
||||
family
|
||||
.forEach { (mov: Movement, input: PlayerInput) in
|
||||
|
||||
// position & velocity component for the current entity
|
||||
|
||||
// get properties
|
||||
_ = mov.position
|
||||
_ = mov.velocity
|
||||
|
||||
// set properties
|
||||
mov.position.x = mov.position.x + 3.0
|
||||
...
|
||||
|
||||
// current input command for the given entity
|
||||
_ = input.command
|
||||
...
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func update2() {
|
||||
family
|
||||
.entityAndComponents
|
||||
.forEach { (entity: Entity, mov: Movement, input: PlayerInput) in
|
||||
|
||||
// the current entity instance
|
||||
_ = entity
|
||||
|
||||
// position & velocity component for the current entity
|
||||
|
||||
// get properties
|
||||
_ = mov.position
|
||||
_ = mov.velocity
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func update3() {
|
||||
family
|
||||
.entities
|
||||
.forEach { (entity: Entity) in
|
||||
|
||||
// the current entity instance
|
||||
_ = entity
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 🧑 Singles
|
||||
|
||||
A `Single` on the other hand is a special kind of family that holds exactly **one** entity with exactly **one** component for the entire lifetime of the Nexus. This may come in handy if you have components that have a [Singleton](https://en.wikipedia.org/wiki/Singleton_(mathematics)) character. Single components must conform to the `SingleComponent` protocol and will not be available through regular family iteration.
|
||||
|
||||
```swift
|
||||
final class GameState: SingleComponent {
|
||||
var quitGame: Bool = false
|
||||
}
|
||||
class GameLogicSystem {
|
||||
let gameState: Single<GameState>
|
||||
|
||||
init(nexus: Nexus) {
|
||||
gameState = nexus.single(GameState.self)
|
||||
}
|
||||
|
||||
func update() {
|
||||
// update your game sate here
|
||||
gameState.component.quitGame = true
|
||||
|
||||
// entity access is provided as well
|
||||
_ = gameState.entity
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 🔗 Serialization
|
||||
|
||||
|
||||
To serialize/deserialize entities you must conform their assigned components to the `Codable` protocol.
|
||||
Conforming components can then be serialized per family like this:
|
||||
|
||||
```swift
|
||||
// MyComponent and YourComponent both conform to Component and Codable protocols.
|
||||
let nexus = Nexus()
|
||||
let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self)
|
||||
|
||||
// JSON encode entities from given family.
|
||||
var jsonEncoder = JSONEncoder()
|
||||
let encodedData = try family.encodeMembers(using: &jsonEncoder)
|
||||
|
||||
// Decode entities into given family from JSON.
|
||||
// The decoded entities will be added to the nexus.
|
||||
var jsonDecoder = JSONDecoder()
|
||||
let newEntities = try family.decodeMembers(from: jsonData, using: &jsonDecoder)
|
||||
|
||||
```
|
||||
|
||||
|
|
@ -56,18 +56,35 @@ extension Entity {
|
|||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
///
|
||||
/// A `Comp` instance must be assigned to this entity!
|
||||
/// - Parameter componentKeyPath: The `KeyPath` to the property of the given component.
|
||||
@inlinable
|
||||
public func get<Comp, Value>(valueAt componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
|
||||
self.get(component: Comp.self)![keyPath: componentKeyPath]
|
||||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
///
|
||||
/// A `Comp` instance must be assigned to this entity!
|
||||
/// - Parameter componentKeyPath: The `KeyPath` to the property of the given component.
|
||||
@inlinable
|
||||
public func get<Comp, Value>(valueAt componentKeyPath: KeyPath<Comp, Value?>) -> Value? where Comp: Component {
|
||||
self.get(component: Comp.self)![keyPath: componentKeyPath]
|
||||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
@inlinable
|
||||
public subscript<Comp, Value>(_ componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
|
||||
self.get(valueAt: componentKeyPath)
|
||||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
@inlinable
|
||||
public subscript<Comp, Value>(_ componentKeyPath: KeyPath<Comp, Value?>) -> Value? where Comp: Component {
|
||||
self.get(valueAt: componentKeyPath)
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
|
|
@ -91,6 +108,29 @@ extension Entity {
|
|||
return true
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
|
||||
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newValue: The value to set.
|
||||
/// - componentKeyPath: The `ReferenceWritableKeyPath` to the property of the given component.
|
||||
/// - Returns: Returns true if an action was performed, false otherwise.
|
||||
@inlinable
|
||||
@discardableResult
|
||||
public func set<Comp, Value>(value newValue: Value?, for componentKeyPath: ReferenceWritableKeyPath<Comp, Value?>) -> Bool where Comp: Component & DefaultInitializable {
|
||||
guard has(Comp.self) else {
|
||||
let newInstance = Comp()
|
||||
newInstance[keyPath: componentKeyPath] = newValue
|
||||
return nexus.assign(component: newInstance, entityId: identifier)
|
||||
}
|
||||
|
||||
get(component: Comp.self)![keyPath: componentKeyPath] = newValue
|
||||
return true
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
|
|
@ -101,4 +141,15 @@ extension Entity {
|
|||
get { self.get(valueAt: componentKeyPath) }
|
||||
nonmutating set { self.set(value: newValue, for: componentKeyPath) }
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
|
||||
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
|
||||
@inlinable
|
||||
public subscript<Comp, Value>(_ componentKeyPath: ReferenceWritableKeyPath<Comp, Value?>) -> Value? where Comp: Component & DefaultInitializable {
|
||||
get { self.get(valueAt: componentKeyPath) }
|
||||
nonmutating set { self.set(value: newValue, for: componentKeyPath) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ public struct Entity {
|
|||
/// The unique entity identifier.
|
||||
public private(set) var identifier: EntityIdentifier
|
||||
|
||||
internal init(nexus: Nexus, id: EntityIdentifier) {
|
||||
init(nexus: Nexus, id: EntityIdentifier) {
|
||||
self.nexus = nexus
|
||||
self.identifier = id
|
||||
identifier = id
|
||||
}
|
||||
|
||||
/// Returns the number of components for this entity.
|
||||
|
|
@ -27,9 +27,24 @@ public struct Entity {
|
|||
nexus.count(components: identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func createEntity() -> Entity {
|
||||
nexus.createEntity()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func createEntity(with components: Component...) -> Entity {
|
||||
createEntity(with: components)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func createEntity(with components: some Collection<Component>) -> Entity {
|
||||
nexus.createEntity(with: components)
|
||||
}
|
||||
|
||||
/// Checks if a component with given type is assigned to this entity.
|
||||
/// - Parameter type: the component type.
|
||||
public func has<C>(_ type: C.Type) -> Bool where C: Component {
|
||||
public func has(_ type: (some Component).Type) -> Bool {
|
||||
has(type.identifier)
|
||||
}
|
||||
|
||||
|
|
@ -48,9 +63,7 @@ public struct Entity {
|
|||
/// - Parameter components: one or more components.
|
||||
@discardableResult
|
||||
public func assign(_ components: Component...) -> Entity {
|
||||
for component: Component in components {
|
||||
assign(component)
|
||||
}
|
||||
assign(components)
|
||||
return self
|
||||
}
|
||||
|
||||
|
|
@ -65,22 +78,28 @@ public struct Entity {
|
|||
/// Add a typed component to this entity.
|
||||
/// - Parameter component: the typed component.
|
||||
@discardableResult
|
||||
public func assign<C>(_ component: C) -> Entity where C: Component {
|
||||
nexus.assign(component: component, to: self)
|
||||
public func assign(_ component: some Component) -> Entity {
|
||||
assign(component)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func assign(_ components: some Collection<Component>) -> Entity {
|
||||
nexus.assign(components: components, to: self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// Remove a component from this entity.
|
||||
/// - Parameter component: the component.
|
||||
@discardableResult
|
||||
public func remove<C>(_ component: C) -> Entity where C: Component {
|
||||
public func remove(_ component: some Component) -> Entity {
|
||||
remove(component.identifier)
|
||||
}
|
||||
|
||||
/// Remove a component by type from this entity.
|
||||
/// - Parameter compType: the component type.
|
||||
@discardableResult
|
||||
public func remove<C>(_ compType: C.Type) -> Entity where C: Component {
|
||||
public func remove(_ compType: (some Component).Type) -> Entity {
|
||||
remove(compType.identifier)
|
||||
}
|
||||
|
||||
|
|
@ -107,34 +126,27 @@ public struct Entity {
|
|||
public func makeComponentsIterator() -> ComponentsIterator {
|
||||
ComponentsIterator(nexus: nexus, entityIdentifier: identifier)
|
||||
}
|
||||
|
||||
/// Returns a sequence of all componenents of this entity.
|
||||
@inlinable
|
||||
public func allComponents() -> AnySequence<Component> {
|
||||
AnySequence { self.makeComponentsIterator() }
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity {
|
||||
public struct ComponentsIterator: IteratorProtocol {
|
||||
private var iterator: AnyIterator<Component>
|
||||
private var iterator: IndexingIterator<[Component]>?
|
||||
|
||||
@usableFromInline
|
||||
init(nexus: Nexus, entityIdentifier: EntityIdentifier) {
|
||||
if let comps = nexus.get(components: entityIdentifier) {
|
||||
iterator = AnyIterator<Component>(comps.compactMap { nexus.get(unsafe: $0, for: entityIdentifier) }.makeIterator())
|
||||
} else {
|
||||
iterator = AnyIterator { nil }
|
||||
}
|
||||
iterator = nexus.get(components: entityIdentifier)?
|
||||
.map { nexus.get(unsafe: $0, for: entityIdentifier) }
|
||||
.makeIterator()
|
||||
}
|
||||
|
||||
public mutating func next() -> Component? {
|
||||
iterator.next()
|
||||
iterator?.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
extension Entity.ComponentsIterator: LazySequenceProtocol { }
|
||||
extension Entity.ComponentsIterator: Sequence { }
|
||||
|
||||
extension Entity.ComponentsIterator: LazySequenceProtocol {}
|
||||
extension Entity.ComponentsIterator: Sequence {}
|
||||
|
||||
extension Entity: Equatable {
|
||||
public static func == (lhs: Entity, rhs: Entity) -> Bool {
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public struct EntityIdentifier {
|
|||
}
|
||||
}
|
||||
|
||||
extension EntityIdentifier: Equatable { }
|
||||
extension EntityIdentifier: Hashable { }
|
||||
extension EntityIdentifier: Equatable {}
|
||||
extension EntityIdentifier: Hashable {}
|
||||
|
||||
extension EntityIdentifier: RawRepresentable {
|
||||
/// The entity identifier represented as a raw value.
|
||||
|
|
@ -33,7 +33,7 @@ extension EntityIdentifier: RawRepresentable {
|
|||
|
||||
@inlinable
|
||||
public init(rawValue: Identifier) {
|
||||
self.id = rawValue
|
||||
id = rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ public struct LinearIncrementingEntityIdGenerator: EntityIdentifierGenerator {
|
|||
|
||||
@usableFromInline
|
||||
init<EntityIds>(startProviding initialEntityIds: EntityIds) where EntityIds: BidirectionalCollection, EntityIds.Element == EntityIdentifier {
|
||||
let initialInUse: [EntityIdentifier.Identifier] = initialEntityIds.map { $0.id }
|
||||
let initialInUse: [EntityIdentifier.Identifier] = initialEntityIds.map(\.id)
|
||||
let maxInUseValue = initialInUse.max() ?? 0
|
||||
let inUseSet = Set(initialInUse) // a set of all eIds in use
|
||||
let allSet = Set(0...maxInUseValue) // all eIds from 0 to including maxInUseValue
|
||||
let allSet = Set(0 ... maxInUseValue) // all eIds from 0 to including maxInUseValue
|
||||
let freeSet = allSet.subtracting(inUseSet) // all "holes" / unused / free eIds
|
||||
let initialFree = Array(freeSet).sorted().reversed() // order them to provide them linear increasing after all initially used are provided.
|
||||
stack = initialFree + initialInUse
|
||||
|
|
@ -83,12 +83,12 @@ public struct LinearIncrementingEntityIdGenerator: EntityIdentifierGenerator {
|
|||
|
||||
@inlinable
|
||||
public init<EntityIds>(startProviding initialEntityIds: EntityIds) where EntityIds: BidirectionalCollection, EntityIds.Element == EntityIdentifier {
|
||||
self.storage = Storage(startProviding: initialEntityIds)
|
||||
storage = Storage(startProviding: initialEntityIds)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init() {
|
||||
self.storage = Storage()
|
||||
storage = Storage()
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
|
|
|
|||
|
|
@ -97,9 +97,7 @@ extension ComponentTypeProvider: ComponentProvider {
|
|||
/// This component provider always returns the same instance of the component. The instance
|
||||
/// is created when first required and is of the type passed in to the initializer.
|
||||
public final class ComponentSingletonProvider {
|
||||
private lazy var instance: Component = {
|
||||
componentType.init()
|
||||
}()
|
||||
private lazy var instance: Component = componentType.init()
|
||||
|
||||
private var componentType: ComponentInitializable.Type
|
||||
|
||||
|
|
@ -171,7 +169,7 @@ extension DynamicComponentProvider: ComponentProvider {
|
|||
/// Represents a state for an EntityStateMachine. The state contains any number of ComponentProviders which
|
||||
/// are used to add components to the entity when this state is entered.
|
||||
public class EntityState {
|
||||
internal var providers = [ComponentIdentifier: ComponentProvider]()
|
||||
var providers = [ComponentIdentifier: ComponentProvider]()
|
||||
|
||||
public init() {}
|
||||
|
||||
|
|
@ -253,7 +251,7 @@ extension EntityState {
|
|||
/// - Returns: This EntityState, so more modifications can be applied.
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
public func addProvider<C: ComponentInitializable>(type: C.Type, provider: ComponentProvider) -> Self {
|
||||
public func addProvider(type: (some ComponentInitializable).Type, provider: ComponentProvider) -> Self {
|
||||
addMapping(for: type).withProvider(provider)
|
||||
return self
|
||||
}
|
||||
|
|
@ -272,7 +270,7 @@ public class StateComponentMapping {
|
|||
/// by more specific mappings if other methods are called.
|
||||
/// - Parameter creatingState: The EntityState that the mapping will belong to
|
||||
/// - Parameter type: The component type for the mapping
|
||||
internal init(creatingState: EntityState, type: ComponentInitializable.Type) {
|
||||
init(creatingState: EntityState, type: ComponentInitializable.Type) {
|
||||
self.creatingState = creatingState
|
||||
componentType = type
|
||||
provider = ComponentTypeProvider(type: type)
|
||||
|
|
@ -318,7 +316,7 @@ public class StateComponentMapping {
|
|||
/// - Parameter closure: The Closure instance to return the component instance
|
||||
/// - Returns: This ComponentMapping, so more modifications can be applied
|
||||
@discardableResult
|
||||
public func withMethod<C: Component>(_ closure: DynamicComponentProvider<C>.Closure) -> Self {
|
||||
public func withMethod(_ closure: DynamicComponentProvider<some Component>.Closure) -> Self {
|
||||
setProvider(DynamicComponentProvider(closure: closure))
|
||||
return self
|
||||
}
|
||||
|
|
@ -403,7 +401,7 @@ public class EntityStateMachine<StateIdentifier: Hashable> {
|
|||
|
||||
var toAdd: [ComponentIdentifier: ComponentProvider]
|
||||
|
||||
if let currentState = currentState {
|
||||
if let currentState {
|
||||
toAdd = .init()
|
||||
for (identifier, provider) in newState.providers {
|
||||
toAdd[identifier] = provider
|
||||
|
|
@ -411,7 +409,8 @@ public class EntityStateMachine<StateIdentifier: Hashable> {
|
|||
|
||||
for (identifier, _) in currentState.providers {
|
||||
if let other = toAdd[identifier], let current = currentState.providers[identifier],
|
||||
current.identifier == other.identifier {
|
||||
current.identifier == other.identifier
|
||||
{
|
||||
toAdd[identifier] = nil
|
||||
} else {
|
||||
entity.remove(identifier)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ extension CodingUserInfoKey {
|
|||
}
|
||||
|
||||
// MARK: - encoding
|
||||
|
||||
extension FamilyMemberContainer: Encodable where R: FamilyEncoding {
|
||||
func encode(to encoder: Encoder) throws {
|
||||
let strategy = encoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy()
|
||||
|
|
@ -50,11 +51,12 @@ extension Family where R: FamilyEncoding {
|
|||
}
|
||||
|
||||
// MARK: - decoding
|
||||
|
||||
extension FamilyMemberContainer: Decodable where R: FamilyDecoding {
|
||||
init(from decoder: Decoder) throws {
|
||||
var familyContainer = try decoder.unkeyedContainer()
|
||||
let strategy = decoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy()
|
||||
self.components = try R.decode(componentsIn: &familyContainer, using: strategy)
|
||||
components = try R.decode(componentsIn: &familyContainer, using: strategy)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public struct Family<R> where R: FamilyRequirementsManaging {
|
|||
/// - Returns: The newly created member entity.
|
||||
@discardableResult
|
||||
public func createMember(@FamilyMemberBuilder<R> using builder: () -> R.Components) -> Entity {
|
||||
self.createMember(with: builder())
|
||||
createMember(with: builder())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,16 +70,17 @@ extension Family: Sequence {
|
|||
}
|
||||
}
|
||||
|
||||
extension Family: LazySequenceProtocol { }
|
||||
extension Family: LazySequenceProtocol {}
|
||||
|
||||
// MARK: - components iterator
|
||||
|
||||
extension Family {
|
||||
public struct ComponentsIterator: IteratorProtocol {
|
||||
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Identifier>.ElementIterator
|
||||
@usableFromInline unowned let nexus: Nexus
|
||||
|
||||
public init(family: Family<R>) {
|
||||
self.nexus = family.nexus
|
||||
nexus = family.nexus
|
||||
memberIdsIterator = family.memberIds.makeIterator()
|
||||
}
|
||||
|
||||
|
|
@ -93,10 +94,11 @@ extension Family {
|
|||
}
|
||||
}
|
||||
|
||||
extension Family.ComponentsIterator: LazySequenceProtocol { }
|
||||
extension Family.ComponentsIterator: Sequence { }
|
||||
extension Family.ComponentsIterator: LazySequenceProtocol {}
|
||||
extension Family.ComponentsIterator: Sequence {}
|
||||
|
||||
// MARK: - entity iterator
|
||||
|
||||
extension Family {
|
||||
@inlinable public var entities: EntityIterator {
|
||||
EntityIterator(family: self)
|
||||
|
|
@ -107,7 +109,7 @@ extension Family {
|
|||
@usableFromInline unowned let nexus: Nexus
|
||||
|
||||
public init(family: Family<R>) {
|
||||
self.nexus = family.nexus
|
||||
nexus = family.nexus
|
||||
memberIdsIterator = family.memberIds.makeIterator()
|
||||
}
|
||||
|
||||
|
|
@ -120,10 +122,11 @@ extension Family {
|
|||
}
|
||||
}
|
||||
|
||||
extension Family.EntityIterator: LazySequenceProtocol { }
|
||||
extension Family.EntityIterator: Sequence { }
|
||||
extension Family.EntityIterator: LazySequenceProtocol {}
|
||||
extension Family.EntityIterator: Sequence {}
|
||||
|
||||
// MARK: - entity component iterator
|
||||
|
||||
extension Family {
|
||||
@inlinable public var entityAndComponents: EntityComponentIterator {
|
||||
EntityComponentIterator(family: self)
|
||||
|
|
@ -134,7 +137,7 @@ extension Family {
|
|||
@usableFromInline unowned let nexus: Nexus
|
||||
|
||||
public init(family: Family<R>) {
|
||||
self.nexus = family.nexus
|
||||
nexus = family.nexus
|
||||
memberIdsIterator = family.memberIds.makeIterator()
|
||||
}
|
||||
|
||||
|
|
@ -147,10 +150,11 @@ extension Family {
|
|||
}
|
||||
}
|
||||
|
||||
extension Family.EntityComponentIterator: LazySequenceProtocol { }
|
||||
extension Family.EntityComponentIterator: Sequence { }
|
||||
extension Family.EntityComponentIterator: LazySequenceProtocol {}
|
||||
extension Family.EntityComponentIterator: Sequence {}
|
||||
|
||||
// MARK: - member creation
|
||||
|
||||
extension Family {
|
||||
/// Create a new entity with components required by this family.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
// Created by Christian Treffs on 07.08.20.
|
||||
//
|
||||
|
||||
@_functionBuilder
|
||||
public enum FamilyMemberBuilderPreview<R> where R: FamilyRequirementsManaging { }
|
||||
public typealias FamilyMemberBuilder<R> = FamilyMemberBuilderPreview<R> where R: FamilyRequirementsManaging
|
||||
#if swift(<5.4)
|
||||
@_functionBuilder
|
||||
public enum FamilyMemberBuilder<R> where R: FamilyRequirementsManaging {}
|
||||
#else
|
||||
@resultBuilder
|
||||
public enum FamilyMemberBuilder<R> where R: FamilyRequirementsManaging {}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public struct FamilyTraitSet {
|
|||
|
||||
self.requiresAll = requiresAll
|
||||
self.excludesAll = excludesAll
|
||||
self.setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
|
||||
setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
|
||||
}
|
||||
|
||||
@inlinable
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
//
|
||||
|
||||
#if canImport(Foundation)
|
||||
import Foundation
|
||||
import Foundation
|
||||
|
||||
extension JSONEncoder: TopLevelEncoder { }
|
||||
extension JSONDecoder: TopLevelDecoder { }
|
||||
extension JSONEncoder: TopLevelEncoder {}
|
||||
extension JSONDecoder: TopLevelDecoder {}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Generated using Sourcery 1.0.0 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// Generated using Sourcery 2.2.5 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// DO NOT EDIT
|
||||
|
||||
// swiftlint:disable file_length
|
||||
// swiftlint:disable function_parameter_count
|
||||
// swiftlint:disable large_tuple
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
//
|
||||
|
||||
#if arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) // 64 bit
|
||||
private let kFibA: UInt = 0x9e3779b97f4a7c15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio)
|
||||
private let kFibA: UInt = 0x9E37_79B9_7F4A_7C15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio)
|
||||
#elseif arch(i386) || arch(arm) || os(watchOS) || arch(wasm32) // 32 bit
|
||||
private let kFibA: UInt = 0x9e3779b9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio)
|
||||
private let kFibA: UInt = 0x9E37_79B9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio)
|
||||
#else
|
||||
#error("unsupported architecture")
|
||||
#error("unsupported architecture")
|
||||
#endif
|
||||
|
||||
/// entity id ^ component identifier hash
|
||||
|
|
@ -20,8 +20,11 @@ public typealias EntityComponentHash = Int
|
|||
public typealias ComponentTypeHash = Int
|
||||
|
||||
// MARK: - hash combine
|
||||
/// Calculates the combined hash of two values. This implementation is based on boost::hash_combine.
|
||||
/// Will always produce the same result for the same combination of seed and value during the single run of a program.
|
||||
|
||||
/// Calculates the combined hash of two values.
|
||||
///
|
||||
/// This implementation is based on boost::hash_combine.
|
||||
/// It produces the same result for the same combination of seed and value during the single run of a program.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - seed: seed hash.
|
||||
|
|
@ -45,8 +48,10 @@ public func hash(combine seed: Int, _ value: Int) -> Int {
|
|||
return Int(bitPattern: uSeed)
|
||||
}
|
||||
|
||||
/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range.
|
||||
/// Is sensitive to the order of the elements.
|
||||
/// Calculates the combined hash value of the elements.
|
||||
///
|
||||
/// This implementation is based on boost::hash_range.
|
||||
/// The hash value this method computes is sensitive to the order of the elements.
|
||||
/// - Parameter hashValues: sequence of hash values to combine.
|
||||
/// - Returns: combined hash value.
|
||||
public func hash<H: Sequence>(combine hashValues: H) -> Int where H.Element: Hashable {
|
||||
|
|
@ -55,45 +60,49 @@ public func hash<H: Sequence>(combine hashValues: H) -> Int where H.Element: Has
|
|||
}
|
||||
|
||||
// MARK: - entity component hash
|
||||
|
||||
extension EntityComponentHash {
|
||||
internal static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
|
||||
static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
|
||||
let entityIdSwapped = UInt(entityId.id).byteSwapped // needs to be 64 bit
|
||||
let componentTypeHashUInt = UInt(bitPattern: componentTypeHash)
|
||||
let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped
|
||||
return Int(bitPattern: hashUInt)
|
||||
}
|
||||
|
||||
internal static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
|
||||
static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
|
||||
let entityIdSwapped = UInt(entityId.id).byteSwapped
|
||||
let entityIdSwappedInt = Int(bitPattern: entityIdSwapped)
|
||||
return hash ^ entityIdSwappedInt
|
||||
}
|
||||
|
||||
internal static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
|
||||
static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
|
||||
let entityId: Int = (hash ^ componentTypeHash).byteSwapped
|
||||
return EntityIdentifier(UInt32(truncatingIfNeeded: entityId))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - string hashing
|
||||
/// <https://stackoverflow.com/a/52440609>
|
||||
|
||||
/// A type that provides stable hash values for String.
|
||||
///
|
||||
/// The details are based on [StackOverflow Q&A on String hashing in Swift](https://stackoverflow.com/a/52440609)
|
||||
public enum StringHashing {
|
||||
/// *Waren Singer djb2*
|
||||
/// *Warren Stringer djb2*
|
||||
///
|
||||
/// <https://stackoverflow.com/a/43149500>
|
||||
/// Implementation from <https://stackoverflow.com/a/43149500>
|
||||
public static func singer_djb2(_ utf8String: String) -> UInt64 {
|
||||
var hash: UInt64 = 5381
|
||||
var iter = utf8String.unicodeScalars.makeIterator()
|
||||
while let char = iter.next() {
|
||||
hash = 127 * (hash & 0xFFFFFFFFFFFFFF) &+ UInt64(char.value)
|
||||
hash = 127 * (hash & 0xFF_FFFF_FFFF_FFFF) &+ UInt64(char.value)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
/// *Dan Bernstein djb2*
|
||||
///
|
||||
/// This algorithm (k=33) was first reported by dan bernstein many years ago in comp.lang.c.
|
||||
/// Another version of this algorithm (now favored by bernstein) uses xor: hash(i) = hash(i - 1) * 33 ^ str[i];
|
||||
/// This algorithm (k=33) was first reported by Dan Bernstein many years ago in `comp.lang.c`.
|
||||
/// Another version of this algorithm (now favored by Bernstein) uses xor: `hash(i) = hash(i - 1) * 33 ^ str[i];`
|
||||
/// The magic of number 33 (why it works better than many other constants, prime or not) has never been adequately explained.
|
||||
///
|
||||
/// <http://www.cse.yorku.ca/~oz/hash.html>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Christian Treffs on 28.10.17.
|
||||
//
|
||||
/// A type that provides a managed contiguous array of elements that you provide.
|
||||
public struct ManagedContiguousArray<Element> {
|
||||
public typealias Index = Int
|
||||
|
||||
|
|
@ -11,16 +12,24 @@ public struct ManagedContiguousArray<Element> {
|
|||
@usableFromInline var size: Int = 0
|
||||
@usableFromInline var store: ContiguousArray<Element?> = []
|
||||
|
||||
/// Creates a new array.
|
||||
/// - Parameter minCount: The minimum number of elements, which defaults to `4096`.
|
||||
public init(minCount: Int = 4096) {
|
||||
chunkSize = minCount
|
||||
store = ContiguousArray<Element?>(repeating: nil, count: minCount)
|
||||
}
|
||||
|
||||
/// The number of elements in the array.
|
||||
@inline(__always)
|
||||
public var count: Int {
|
||||
size
|
||||
}
|
||||
|
||||
/// Inserts an element into the managed array.
|
||||
/// - Parameters:
|
||||
/// - element: The element to insert
|
||||
/// - index: The location at which to insert the element.
|
||||
/// - Returns: `true` to indicate the element was inserted.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func insert(_ element: Element, at index: Int) -> Bool {
|
||||
|
|
@ -34,6 +43,8 @@ public struct ManagedContiguousArray<Element> {
|
|||
return true
|
||||
}
|
||||
|
||||
/// Returns a Boolean value that indicates whether the index location holds an element.
|
||||
/// - Parameter index: The index location in the contiguous array to inspect.
|
||||
@inlinable
|
||||
public func contains(_ index: Index) -> Bool {
|
||||
if store.count <= index {
|
||||
|
|
@ -42,16 +53,25 @@ public struct ManagedContiguousArray<Element> {
|
|||
return store[index] != nil
|
||||
}
|
||||
|
||||
/// Retrieves the value at the index location you provide.
|
||||
/// - Parameter index: The index location.
|
||||
/// - Returns: The element at the index location, or `nil`.
|
||||
@inline(__always)
|
||||
public func get(at index: Index) -> Element? {
|
||||
store[index]
|
||||
}
|
||||
|
||||
/// Unsafely retrieves the value at the index location you provide.
|
||||
/// - Parameter index: The index location.
|
||||
/// - Returns: The element at the index location.
|
||||
@inline(__always)
|
||||
public func get(unsafeAt index: Index) -> Element {
|
||||
store[index].unsafelyUnwrapped
|
||||
}
|
||||
|
||||
/// Removes the object at the index location you provide.
|
||||
/// - Parameter index: The index location.
|
||||
/// - Returns: `true` to indicate the element was removed.
|
||||
@discardableResult
|
||||
@inlinable
|
||||
public mutating func remove(at index: Index) -> Bool {
|
||||
|
|
@ -65,17 +85,23 @@ public struct ManagedContiguousArray<Element> {
|
|||
return true
|
||||
}
|
||||
|
||||
/// Clears the array of all elements.
|
||||
/// - Parameter keepingCapacity: A Boolean value that indicates whether to keep the capacity of the array.
|
||||
@inlinable
|
||||
public mutating func clear(keepingCapacity: Bool = false) {
|
||||
size = 0
|
||||
store.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
|
||||
/// Returns a Boolean value that indicates if the array needs to grow to insert another item.
|
||||
/// - Parameter index: The index location to check.
|
||||
@inlinable
|
||||
func needsToGrow(_ index: Index) -> Bool {
|
||||
index > store.count - 1
|
||||
}
|
||||
|
||||
/// Expands the contiguous array to encompass the index location you provide.
|
||||
/// - Parameter index: The index location.
|
||||
@inlinable
|
||||
mutating func grow(to index: Index) {
|
||||
let newCapacity: Int = calculateCapacity(to: index)
|
||||
|
|
@ -83,6 +109,8 @@ public struct ManagedContiguousArray<Element> {
|
|||
store += ContiguousArray<Element?>(repeating: nil, count: newCount)
|
||||
}
|
||||
|
||||
/// Returns the capacity of the array to the index location you provide.
|
||||
/// - Parameter index: The index location
|
||||
@inlinable
|
||||
func calculateCapacity(to index: Index) -> Int {
|
||||
let delta = Float(index) / Float(chunkSize)
|
||||
|
|
@ -92,6 +120,7 @@ public struct ManagedContiguousArray<Element> {
|
|||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
extension ManagedContiguousArray: Equatable where Element: Equatable {
|
||||
public static func == (lhs: ManagedContiguousArray<Element>, rhs: ManagedContiguousArray<Element>) -> Bool {
|
||||
lhs.store == rhs.store
|
||||
|
|
@ -99,4 +128,5 @@ extension ManagedContiguousArray: Equatable where Element: Equatable {
|
|||
}
|
||||
|
||||
// MARK: - Codable
|
||||
extension ManagedContiguousArray: Codable where Element: Codable { }
|
||||
|
||||
extension ManagedContiguousArray: Codable where Element: Codable {}
|
||||
|
|
|
|||
|
|
@ -24,15 +24,19 @@ extension Nexus {
|
|||
@discardableResult
|
||||
public final func assign(component: Component, to entity: Entity) -> Bool {
|
||||
let entityId: EntityIdentifier = entity.identifier
|
||||
defer { delegate?.nexusEvent(ComponentAdded(component: component.identifier, toEntity: entity.identifier)) }
|
||||
return assign(component: component, entityId: entityId)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func assign<C>(component: C, to entity: Entity) -> Bool where C: Component {
|
||||
public final func assign(component: some Component, to entity: Entity) -> Bool {
|
||||
assign(component: component, to: entity)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public final func assign(components: some Collection<Component>, to entity: Entity) -> Bool {
|
||||
assign(components: components, to: entity.identifier)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public final func get(safe componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? {
|
||||
guard let uniformComponents = componentsByType[componentId], uniformComponents.contains(entityId.index) else {
|
||||
|
|
@ -89,7 +93,7 @@ extension Nexus {
|
|||
return false
|
||||
}
|
||||
var iter = allComponents.makeIterator()
|
||||
var removedAll: Bool = true
|
||||
var removedAll = true
|
||||
while let component = iter.next() {
|
||||
removedAll = removedAll && remove(component: component, from: entityId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@
|
|||
// Created by Christian Treffs on 30.07.20.
|
||||
//
|
||||
|
||||
@_functionBuilder
|
||||
public enum ComponentsBuilderPreview { }
|
||||
public typealias ComponentsBuilder = ComponentsBuilderPreview
|
||||
#if swift(<5.4)
|
||||
@_functionBuilder
|
||||
public enum ComponentsBuilder {}
|
||||
#else
|
||||
@resultBuilder
|
||||
public enum ComponentsBuilder {}
|
||||
#endif
|
||||
|
||||
extension ComponentsBuilder {
|
||||
public static func buildBlock(_ components: Component...) -> [Component] {
|
||||
|
|
@ -35,7 +39,7 @@ extension Nexus {
|
|||
/// - Returns: The newly created entity with the provided component assigned.
|
||||
@discardableResult
|
||||
public func createEntity(@ComponentsBuilder using builder: () -> Component) -> Entity {
|
||||
self.createEntity(with: builder())
|
||||
createEntity(with: builder())
|
||||
}
|
||||
|
||||
/// Create an entity assigning multiple components.
|
||||
|
|
@ -51,7 +55,7 @@ extension Nexus {
|
|||
/// - Returns: The newly created entity with the provided components assigned.
|
||||
@discardableResult
|
||||
public func createEntity(@ComponentsBuilder using builder: () -> [Component]) -> Entity {
|
||||
self.createEntity(with: builder())
|
||||
createEntity(with: builder())
|
||||
}
|
||||
|
||||
/// Create multiple entities assigning one component each.
|
||||
|
|
@ -68,7 +72,7 @@ extension Nexus {
|
|||
/// - Returns: The newly created entities with the provided component assigned.
|
||||
@discardableResult
|
||||
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> Component) -> [Entity] {
|
||||
(0..<count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||
(0 ..< count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||
}
|
||||
|
||||
/// Create multiple entities assigning multiple components each.
|
||||
|
|
@ -85,7 +89,7 @@ extension Nexus {
|
|||
/// - builder: The component builder providing context.
|
||||
/// - Returns: The newly created entities with the provided components assigned.
|
||||
@discardableResult
|
||||
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> [Component]) -> [Entity] {
|
||||
(0..<count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> [Component] = { _ in [] }) -> [Entity] {
|
||||
(0 ..< count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ extension Nexus {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
public func createEntity<C>(with components: C) -> Entity where C: Collection, C.Element == Component {
|
||||
let entity = self.createEntity()
|
||||
public func createEntity(with components: some Collection<Component>) -> Entity {
|
||||
let entity = createEntity()
|
||||
assign(components: components, to: entity.identifier)
|
||||
return entity
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ extension Nexus {
|
|||
|
||||
@discardableResult
|
||||
public func destroy(entity: Entity) -> Bool {
|
||||
self.destroy(entityId: entity.identifier)
|
||||
destroy(entityId: entity.identifier)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
|
|
@ -76,6 +76,7 @@ extension Nexus {
|
|||
}
|
||||
|
||||
// MARK: - entities iterator
|
||||
|
||||
extension Nexus {
|
||||
public struct EntitiesIterator: IteratorProtocol {
|
||||
private var iterator: AnyIterator<Entity>
|
||||
|
|
@ -96,5 +97,6 @@ extension Nexus {
|
|||
}
|
||||
}
|
||||
}
|
||||
extension Nexus.EntitiesIterator: LazySequenceProtocol { }
|
||||
extension Nexus.EntitiesIterator: Sequence { }
|
||||
|
||||
extension Nexus.EntitiesIterator: LazySequenceProtocol {}
|
||||
extension Nexus.EntitiesIterator: Sequence {}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
extension Nexus {
|
||||
@usableFromInline
|
||||
func assign<C>(components: C, to entityId: EntityIdentifier) where C: Collection, C.Element == Component {
|
||||
@discardableResult
|
||||
func assign(components: some Collection<Component>, to entityId: EntityIdentifier) -> Bool {
|
||||
var iter = components.makeIterator()
|
||||
while let component = iter.next() {
|
||||
let componentId = component.identifier
|
||||
|
|
@ -15,7 +16,7 @@ extension Nexus {
|
|||
guard !has(componentId: componentId, entityId: entityId) else {
|
||||
delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||
assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// add component instances to uniform component stores
|
||||
|
|
@ -27,6 +28,7 @@ extension Nexus {
|
|||
|
||||
// Update entity membership
|
||||
update(familyMembership: entityId)
|
||||
return true
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
|
|
@ -61,7 +63,10 @@ extension Nexus {
|
|||
|
||||
@usableFromInline
|
||||
func assign(_ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) {
|
||||
componentIdsByEntity[entityId]!.insert(componentId)
|
||||
let (inserted, _) = componentIdsByEntity[entityId]!.insert(componentId)
|
||||
if inserted {
|
||||
delegate?.nexusEvent(ComponentAdded(component: componentId, toEntity: entityId))
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
|
|
@ -98,8 +103,8 @@ extension Nexus {
|
|||
return
|
||||
}
|
||||
|
||||
let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits)
|
||||
if !exists(entity: entityId) && isMember {
|
||||
let isMember: Bool = isMember(entity: entityId, inFamilyWithTraits: traits)
|
||||
if !exists(entity: entityId), isMember {
|
||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,14 @@ public final class Nexus {
|
|||
codingStrategy: DefaultCodingStrategy())
|
||||
}
|
||||
|
||||
internal init(componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>],
|
||||
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
|
||||
entityIdGenerator: EntityIdentifierGenerator,
|
||||
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Identifier>],
|
||||
codingStrategy: CodingStrategy) {
|
||||
init(componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>],
|
||||
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
|
||||
entityIdGenerator: EntityIdentifierGenerator,
|
||||
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Identifier>],
|
||||
codingStrategy: CodingStrategy)
|
||||
{
|
||||
self.componentsByType = componentsByType
|
||||
self.componentIdsByEntity = componentsByEntity
|
||||
componentIdsByEntity = componentsByEntity
|
||||
self.familyMembersByTraits = familyMembersByTraits
|
||||
self.entityIdGenerator = entityIdGenerator
|
||||
self.codingStrategy = codingStrategy
|
||||
|
|
@ -66,6 +67,7 @@ public final class Nexus {
|
|||
}
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Nexus: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
"<Nexus entities:\(numEntities) components:\(numComponents) families:\(numFamilies)>"
|
||||
|
|
@ -73,8 +75,9 @@ extension Nexus: CustomDebugStringConvertible {
|
|||
}
|
||||
|
||||
// MARK: - default coding strategy
|
||||
|
||||
public struct DefaultCodingStrategy: CodingStrategy {
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
public func codingKey<C>(for componentType: C.Type) -> DynamicCodingKey where C: Component {
|
||||
DynamicCodingKey(stringValue: "\(C.self)").unsafelyUnwrapped
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// NexusEvents.swift
|
||||
// NexusEvent.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 08.10.17.
|
||||
|
|
@ -34,13 +34,3 @@ public struct FamilyMemberRemoved: NexusEvent {
|
|||
public let member: EntityIdentifier
|
||||
public let from: FamilyTraitSet
|
||||
}
|
||||
|
||||
public struct ChildAdded: NexusEvent {
|
||||
public let parent: EntityIdentifier
|
||||
public let child: EntityIdentifier
|
||||
}
|
||||
|
||||
public struct ChildRemoved: NexusEvent {
|
||||
public let parent: EntityIdentifier
|
||||
public let child: EntityIdentifier
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ extension Single where A: SingleComponent {
|
|||
}
|
||||
|
||||
public var entity: Entity {
|
||||
Entity(nexus: self.nexus, id: entityId)
|
||||
Entity(nexus: nexus, id: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
extension Nexus {
|
||||
public func single<S>(_ component: S.Type) -> Single<S> where S: SingleComponent {
|
||||
let family = self.family(requires: S.self)
|
||||
let family = family(requires: S.self)
|
||||
precondition(family.count <= 1, "Singleton count of \(S.self) must be 0 or 1: \(family.count)")
|
||||
let entityId: EntityIdentifier
|
||||
if family.isEmpty {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Christian Treffs on 30.10.17.
|
||||
//
|
||||
|
||||
/// An (unordered) sparse set.
|
||||
/// An unordered sparse set.
|
||||
///
|
||||
/// - `Element`: the element (instance) to store.
|
||||
/// - `Key`: the unique, hashable datastructure to use as a key to retrieve
|
||||
|
|
@ -74,7 +74,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
guard let denseIndex = findIndex(at: key) else {
|
||||
return nil
|
||||
}
|
||||
let entry = self.dense[denseIndex]
|
||||
let entry = dense[denseIndex]
|
||||
assert(entry.key == key, "entry.key and findIndex(at: key) must be equal!")
|
||||
return entry.element
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
}
|
||||
|
||||
let removed = swapRemove(at: denseIndex)
|
||||
if !dense.isEmpty && denseIndex < dense.count {
|
||||
if !dense.isEmpty, denseIndex < dense.count {
|
||||
let swappedElement = dense[denseIndex]
|
||||
sparse[swappedElement.key] = denseIndex
|
||||
}
|
||||
|
|
@ -130,6 +130,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new sparse set.
|
||||
public init() {
|
||||
self.init(storage: Storage())
|
||||
}
|
||||
|
|
@ -141,21 +142,26 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
|
||||
@usableFromInline let storage: Storage
|
||||
|
||||
/// The size of the set.
|
||||
public var count: Int { storage.count }
|
||||
/// A Boolean value that indicates whether the set is empty.
|
||||
public var isEmpty: Bool { storage.isEmpty }
|
||||
|
||||
/// Returns a Boolean value that indicates whether the key is included in the set.
|
||||
/// - Parameter key: The key to inspect.
|
||||
@inlinable
|
||||
public func contains(_ key: Key) -> Bool {
|
||||
storage.findIndex(at: key) != nil
|
||||
}
|
||||
|
||||
/// Inset an element for a given key into the set in O(1).
|
||||
///
|
||||
/// Elements at previously set keys will be replaced.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - element: the element
|
||||
/// - key: the key
|
||||
/// - Returns: true if new, false if replaced.
|
||||
/// - element: The element.
|
||||
/// - key: The key.
|
||||
/// - Returns: `true` if new, `false` if replaced.
|
||||
@discardableResult
|
||||
public func insert(_ element: Element, at key: Key) -> Bool {
|
||||
storage.insert(element, at: key)
|
||||
|
|
@ -163,13 +169,16 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
|
||||
/// Get the element for the given key in O(1).
|
||||
///
|
||||
/// - Parameter key: the key
|
||||
/// - Returns: the element or nil of key not found.
|
||||
/// - Parameter key: The key.
|
||||
/// - Returns: the element or `nil` if the key wasn't found.
|
||||
@inlinable
|
||||
public func get(at key: Key) -> Element? {
|
||||
storage.findElement(at: key)
|
||||
}
|
||||
|
||||
/// Unsafely gets the element for the given key,
|
||||
/// - Parameter key: The key.
|
||||
/// - Returns: The element.
|
||||
@inlinable
|
||||
public func get(unsafeAt key: Key) -> Element {
|
||||
storage.findElement(at: key).unsafelyUnwrapped
|
||||
|
|
@ -184,17 +193,21 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
storage.remove(at: key)?.element
|
||||
}
|
||||
|
||||
/// Removes all keys and elements from the set.
|
||||
/// - Parameter keepingCapacity: A Boolean value that indicates whether the set should maintain it's capacity.
|
||||
@inlinable
|
||||
public func removeAll(keepingCapacity: Bool = false) {
|
||||
storage.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
|
||||
/// The first element of the set.
|
||||
@inlinable public var first: Element? {
|
||||
storage.first
|
||||
}
|
||||
}
|
||||
|
||||
extension UnorderedSparseSet where Key == Int {
|
||||
/// Retrieve or set an element using the key.
|
||||
@inlinable
|
||||
public subscript(key: Key) -> Element {
|
||||
get {
|
||||
|
|
@ -208,12 +221,14 @@ extension UnorderedSparseSet where Key == Int {
|
|||
}
|
||||
|
||||
// MARK: - Sequence
|
||||
|
||||
extension UnorderedSparseSet: Sequence {
|
||||
public func makeIterator() -> ElementIterator {
|
||||
ElementIterator(self)
|
||||
}
|
||||
|
||||
// MARK: - UnorderedSparseSetIterator
|
||||
|
||||
public struct ElementIterator: IteratorProtocol {
|
||||
var iterator: IndexingIterator<ContiguousArray<Storage.Entry>>
|
||||
|
||||
|
|
@ -226,17 +241,20 @@ extension UnorderedSparseSet: Sequence {
|
|||
}
|
||||
}
|
||||
}
|
||||
extension UnorderedSparseSet.ElementIterator: LazySequenceProtocol { }
|
||||
extension UnorderedSparseSet.ElementIterator: Sequence { }
|
||||
|
||||
extension UnorderedSparseSet.ElementIterator: LazySequenceProtocol {}
|
||||
extension UnorderedSparseSet.ElementIterator: Sequence {}
|
||||
|
||||
// MARK: - Equatable
|
||||
extension UnorderedSparseSet.Storage.Entry: Equatable where Element: Equatable { }
|
||||
|
||||
extension UnorderedSparseSet.Storage.Entry: Equatable where Element: Equatable {}
|
||||
extension UnorderedSparseSet.Storage: Equatable where Element: Equatable {
|
||||
@usableFromInline
|
||||
static func == (lhs: UnorderedSparseSet<Element, Key>.Storage, rhs: UnorderedSparseSet<Element, Key>.Storage) -> Bool {
|
||||
lhs.dense == rhs.dense && lhs.sparse == rhs.sparse
|
||||
}
|
||||
}
|
||||
|
||||
extension UnorderedSparseSet: Equatable where Element: Equatable {
|
||||
public static func == (lhs: UnorderedSparseSet<Element, Key>, rhs: UnorderedSparseSet<Element, Key>) -> Bool {
|
||||
lhs.storage == rhs.storage
|
||||
|
|
@ -244,6 +262,7 @@ extension UnorderedSparseSet: Equatable where Element: Equatable {
|
|||
}
|
||||
|
||||
// MARK: - Codable
|
||||
extension UnorderedSparseSet.Storage.Entry: Codable where Element: Codable { }
|
||||
extension UnorderedSparseSet.Storage: Codable where Element: Codable { }
|
||||
extension UnorderedSparseSet: Codable where Element: Codable { }
|
||||
|
||||
extension UnorderedSparseSet.Storage.Entry: Codable where Element: Codable {}
|
||||
extension UnorderedSparseSet.Storage: Codable where Element: Codable {}
|
||||
extension UnorderedSparseSet: Codable where Element: Codable {}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// Created by Christian Treffs on 14.02.19.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
|
|
@ -97,3 +98,6 @@ class HashingPerformanceTests: XCTestCase {
|
|||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
#warning("Skipping HashingPerformanceTests")
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// Created by Christian Treffs on 05.10.19.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ final class TypeIdentifierPerformanceTests: XCTestCase {
|
|||
/// release: 1.034 sec
|
||||
/// debug:
|
||||
func testPerformanceHash() {
|
||||
measure {
|
||||
measure(options: .default) {
|
||||
for _ in 0..<maxIterations {
|
||||
_ = StringHashing.singer_djb2(String(describing: Color.self))
|
||||
_ = StringHashing.singer_djb2(String(describing: EmptyComponent.self))
|
||||
|
|
@ -91,3 +92,6 @@ final class TypeIdentifierPerformanceTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#warning("Skipping TypeIdentifierPerformanceTests tests")
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
#if !canImport(ObjectiveC)
|
||||
import XCTest
|
||||
|
||||
extension ComponentIdentifierTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentIdentifierTests = [
|
||||
("testMeasureComponentIdentifier", testMeasureComponentIdentifier),
|
||||
("testMeasureStaticComponentIdentifier", testMeasureStaticComponentIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension HashingPerformanceTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__HashingPerformanceTests = [
|
||||
("testMeasureBernsteinDjb2", testMeasureBernsteinDjb2),
|
||||
("testMeasureCombineHash", testMeasureCombineHash),
|
||||
("testMeasureSDBM", testMeasureSDBM),
|
||||
("testMeasureSetOfSetHash", testMeasureSetOfSetHash),
|
||||
("testMeasureSingerDjb2", testMeasureSingerDjb2),
|
||||
("testMeasureSwiftHasher", testMeasureSwiftHasher)
|
||||
]
|
||||
}
|
||||
|
||||
extension TypeIdentifierPerformanceTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__TypeIdentifierPerformanceTests = [
|
||||
("testPerformanceHash", testPerformanceHash),
|
||||
("testPerformanceMirrorReflectingDescription", testPerformanceMirrorReflectingDescription),
|
||||
("testPerformanceObjectIdentifier", testPerformanceObjectIdentifier),
|
||||
("testPerformanceStringDescribing", testPerformanceStringDescribing),
|
||||
("testPerformanceStringReflecting", testPerformanceStringReflecting)
|
||||
]
|
||||
}
|
||||
|
||||
extension TypedFamilyPerformanceTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__TypedFamilyPerformanceTests = [
|
||||
("testMeasureTraitMatching", testMeasureTraitMatching),
|
||||
("testPerformanceArray", testPerformanceArray),
|
||||
("testPerformanceTypedFamilyEntities", testPerformanceTypedFamilyEntities),
|
||||
("testPerformanceTypedFamilyEntityFiveComponents", testPerformanceTypedFamilyEntityFiveComponents),
|
||||
("testPerformanceTypedFamilyEntityFourComponents", testPerformanceTypedFamilyEntityFourComponents),
|
||||
("testPerformanceTypedFamilyEntityOneComponent", testPerformanceTypedFamilyEntityOneComponent),
|
||||
("testPerformanceTypedFamilyEntityThreeComponents", testPerformanceTypedFamilyEntityThreeComponents),
|
||||
("testPerformanceTypedFamilyEntityTwoComponents", testPerformanceTypedFamilyEntityTwoComponents),
|
||||
("testPerformanceTypedFamilyFiveComponents", testPerformanceTypedFamilyFiveComponents),
|
||||
("testPerformanceTypedFamilyFourComponents", testPerformanceTypedFamilyFourComponents),
|
||||
("testPerformanceTypedFamilyOneComponent", testPerformanceTypedFamilyOneComponent),
|
||||
("testPerformanceTypedFamilyThreeComponents", testPerformanceTypedFamilyThreeComponents),
|
||||
("testPerformanceTypedFamilyTwoComponents", testPerformanceTypedFamilyTwoComponents)
|
||||
]
|
||||
}
|
||||
|
||||
public func __allTests() -> [XCTestCaseEntry] {
|
||||
return [
|
||||
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||
testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests),
|
||||
testCase(TypeIdentifierPerformanceTests.__allTests__TypeIdentifierPerformanceTests),
|
||||
testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests)
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
@ -10,6 +10,29 @@ import FirebladeECS
|
|||
class EmptyComponent: Component {
|
||||
}
|
||||
|
||||
final class Optionals: Component, DefaultInitializable {
|
||||
var int: Int?
|
||||
var float: Float?
|
||||
var string: String?
|
||||
|
||||
convenience init() {
|
||||
self.init(nil, nil, nil)
|
||||
}
|
||||
|
||||
init(_ int: Int?, _ float: Float?, _ string: String?) {
|
||||
self.int = int
|
||||
self.float = float
|
||||
self.string = string
|
||||
}
|
||||
}
|
||||
extension Optionals: Equatable {
|
||||
static func == (lhs: Optionals, rhs: Optionals) -> Bool {
|
||||
lhs.int == rhs.int &&
|
||||
lhs.float == rhs.float &&
|
||||
lhs.string == rhs.string
|
||||
}
|
||||
}
|
||||
|
||||
final class Name: Component, DefaultInitializable {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class EntityTests: XCTestCase {
|
|||
entity.assign(name, vel)
|
||||
|
||||
let expectedComponents: [Component] = [pos, name, vel]
|
||||
let allComponents = entity.allComponents()
|
||||
let allComponents = Array(entity.makeComponentsIterator())
|
||||
|
||||
XCTAssertTrue(allComponents.elementsEqualUnordered(expectedComponents) { $0 === $1 })
|
||||
}
|
||||
|
|
@ -117,6 +117,56 @@ class EntityTests: XCTestCase {
|
|||
entity[Position.self] = nil // remove position component
|
||||
XCTAssertNil(entity[Position.self])
|
||||
|
||||
let opts = Optionals(1, 2, "hello")
|
||||
entity[Optionals.self] = opts
|
||||
XCTAssertEqual(entity[Optionals.self], opts)
|
||||
|
||||
entity[\Optionals.float] = nil
|
||||
XCTAssertEqual(entity[\Optionals.float], nil)
|
||||
XCTAssertEqual(entity[\Optionals.int], 1)
|
||||
XCTAssertEqual(entity[\Optionals.string], "hello")
|
||||
|
||||
entity[Optionals.self] = nil
|
||||
XCTAssertNil(entity[Optionals.self])
|
||||
entity[\Optionals.string] = "world"
|
||||
XCTAssertEqual(entity[\Optionals.string], "world")
|
||||
|
||||
entity.assign(Comp1(12))
|
||||
XCTAssertEqual(entity[\Comp1.value], 12)
|
||||
}
|
||||
|
||||
func testComponentsIteration() {
|
||||
let nexus = Nexus()
|
||||
let entity = nexus.createEntity()
|
||||
XCTAssertTrue(Array(entity.makeComponentsIterator()).isEmpty)
|
||||
entity.assign(Position())
|
||||
XCTAssertEqual(Array(entity.makeComponentsIterator()).count, 1)
|
||||
}
|
||||
|
||||
func testEntityCreationIntrinsic() {
|
||||
let nexus = Nexus()
|
||||
let entity = nexus.createEntity()
|
||||
|
||||
let secondEntity = entity.createEntity()
|
||||
XCTAssertNotEqual(secondEntity, entity)
|
||||
|
||||
let thirdEntity = secondEntity.createEntity()
|
||||
XCTAssertNotEqual(secondEntity, thirdEntity)
|
||||
XCTAssertNotEqual(entity, thirdEntity)
|
||||
|
||||
let entityWithComponents = entity.createEntity(with: Position(), Name())
|
||||
XCTAssertTrue(entityWithComponents.has(Position.self))
|
||||
XCTAssertTrue(entityWithComponents.has(Name.self))
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 4)
|
||||
XCTAssertEqual(nexus.numComponents, 2)
|
||||
}
|
||||
|
||||
func testEntityDescriptions() {
|
||||
let nexus = Nexus()
|
||||
let entt = nexus.createEntity()
|
||||
XCTAssertFalse(entt.description.isEmpty)
|
||||
XCTAssertFalse(entt.debugDescription.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Generated using Sourcery 1.0.0 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// Generated using Sourcery 2.2.5 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// DO NOT EDIT
|
||||
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
|
|
@ -1476,3 +1475,4 @@ extension Comp8: Equatable {
|
|||
}
|
||||
}
|
||||
extension Comp8: Codable { }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
//
|
||||
// NexusEventDelegateTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Christian Treffs on 25.11.20.
|
||||
//
|
||||
|
||||
import FirebladeECS
|
||||
import XCTest
|
||||
|
||||
final class NexusEventDelegateTests: XCTestCase {
|
||||
lazy var nexus = Nexus()
|
||||
fileprivate var delegateTester: DelegateTester!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
delegateTester = nil
|
||||
}
|
||||
|
||||
func testEventEntityCreated() {
|
||||
var entityCreatedEvents: [EntityCreated] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case let entityCreated as EntityCreated:
|
||||
entityCreatedEvents.append(entityCreated)
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
XCTAssertEqual(entityCreatedEvents.count, 0)
|
||||
nexus.createEntity()
|
||||
XCTAssertEqual(entityCreatedEvents.count, 1)
|
||||
nexus.createEntities(count: 100)
|
||||
XCTAssertEqual(entityCreatedEvents.count, 101)
|
||||
}
|
||||
|
||||
func testEventEntityDestroyed() {
|
||||
var events: [EntityDestroyed] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case let event as EntityDestroyed:
|
||||
events.append(event)
|
||||
case _ as EntityCreated:
|
||||
break
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
XCTAssertEqual(events.count, 0)
|
||||
nexus.createEntities(count: 100)
|
||||
XCTAssertEqual(events.count, 0)
|
||||
for entitiy in nexus.makeEntitiesIterator() {
|
||||
entitiy.destroy()
|
||||
}
|
||||
XCTAssertEqual(events.count, 100)
|
||||
}
|
||||
|
||||
func testEventComponentAdded() {
|
||||
var componentsAddedEvents: [ComponentAdded] = []
|
||||
var entityCreatedEvents: [EntityCreated] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case let compAdded as ComponentAdded:
|
||||
componentsAddedEvents.append(compAdded)
|
||||
case let entityCreated as EntityCreated:
|
||||
entityCreatedEvents.append(entityCreated)
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
XCTAssertEqual(componentsAddedEvents.count, 0)
|
||||
XCTAssertEqual(entityCreatedEvents.count, 0)
|
||||
let entity = nexus.createEntity()
|
||||
entity.assign(MyComponent(name: "0", flag: true))
|
||||
XCTAssertEqual(componentsAddedEvents.count, 1)
|
||||
XCTAssertEqual(entityCreatedEvents.count, 1)
|
||||
let entity2 = nexus.createEntity()
|
||||
entity2.assign(MyComponent(name: "0", flag: true), YourComponent(number: 2))
|
||||
XCTAssertEqual(componentsAddedEvents.count, 3)
|
||||
XCTAssertEqual(entityCreatedEvents.count, 2)
|
||||
}
|
||||
|
||||
func testEventComponentRemoved() {
|
||||
var events: [ComponentRemoved] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case let event as ComponentRemoved:
|
||||
events.append(event)
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
let entity = nexus.createEntity()
|
||||
entity.assign(
|
||||
MyComponent(name: "Hello", flag: false),
|
||||
YourComponent(number: 3.14),
|
||||
EmptyComponent()
|
||||
)
|
||||
|
||||
XCTAssertEqual(entity.numComponents, 3)
|
||||
XCTAssertEqual(events.count, 0)
|
||||
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
entity.remove(MyComponent.self)
|
||||
XCTAssertEqual(events.count, 1)
|
||||
XCTAssertEqual(entity.numComponents, 2)
|
||||
|
||||
entity.remove(EmptyComponent.self)
|
||||
XCTAssertEqual(events.count, 2)
|
||||
XCTAssertEqual(entity.numComponents, 1)
|
||||
|
||||
entity.remove(YourComponent.self)
|
||||
XCTAssertEqual(events.count, 3)
|
||||
XCTAssertEqual(entity.numComponents, 0)
|
||||
|
||||
}
|
||||
|
||||
func testFamilyMemeberAdded() {
|
||||
var eventsFamilyMemberRemoved: [FamilyMemberRemoved] = []
|
||||
var eventsComponentRemoved: [ComponentRemoved] = []
|
||||
var eventsEntityDestroyed: [EntityDestroyed] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case is FamilyMemberAdded,
|
||||
is ComponentAdded,
|
||||
is EntityCreated:
|
||||
break
|
||||
case let event as FamilyMemberRemoved:
|
||||
eventsFamilyMemberRemoved.append(event)
|
||||
case let event as ComponentRemoved:
|
||||
eventsComponentRemoved.append(event)
|
||||
case let event as EntityDestroyed:
|
||||
eventsEntityDestroyed.append(event)
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self)
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
family.createMember(with: (MyComponent(name: "Bla", flag: true), YourComponent(number: 85)))
|
||||
family.createMember(with: (MyComponent(name: "Hello", flag: false), YourComponent(number: 05050)))
|
||||
family.createMember(with: (MyComponent(name: "asdasd", flag: true), YourComponent(number: 9494949)))
|
||||
|
||||
XCTAssertEqual(eventsFamilyMemberRemoved.count, 0)
|
||||
XCTAssertEqual(eventsComponentRemoved.count, 0)
|
||||
XCTAssertEqual(family.count, 3)
|
||||
XCTAssertEqual(eventsEntityDestroyed.count, 0)
|
||||
|
||||
XCTAssertTrue(family.destroyMembers())
|
||||
|
||||
XCTAssertEqual(eventsFamilyMemberRemoved.count, 3)
|
||||
XCTAssertEqual(eventsComponentRemoved.count, 6)
|
||||
XCTAssertEqual(family.count, 0)
|
||||
XCTAssertEqual(eventsEntityDestroyed.count, 3)
|
||||
}
|
||||
|
||||
func testFamilyMemberRemoved() {
|
||||
var eventsMemberAdded: [FamilyMemberAdded] = []
|
||||
var eventsComponentAdded: [ComponentAdded] = []
|
||||
var eventsEntityCreated: [EntityCreated] = []
|
||||
delegateTester = DelegateTester(onEvent: { event in
|
||||
switch event {
|
||||
case let event as FamilyMemberAdded:
|
||||
eventsMemberAdded.append(event)
|
||||
case let event as ComponentAdded:
|
||||
eventsComponentAdded.append(event)
|
||||
case let event as EntityCreated:
|
||||
eventsEntityCreated.append(event)
|
||||
default:
|
||||
XCTFail("unexpected event \(event)")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
let family = nexus.family(requiresAll: MyComponent.self, YourComponent.self)
|
||||
nexus.delegate = delegateTester
|
||||
|
||||
XCTAssertEqual(family.count, 0)
|
||||
XCTAssertEqual(eventsMemberAdded.count, 0)
|
||||
XCTAssertEqual(eventsComponentAdded.count, 0)
|
||||
XCTAssertEqual(eventsEntityCreated.count, 0)
|
||||
|
||||
family.createMember(with: (MyComponent(name: "Bla", flag: true), YourComponent(number: 85)))
|
||||
XCTAssertEqual(family.count, 1)
|
||||
XCTAssertEqual(eventsMemberAdded.count, 1)
|
||||
XCTAssertEqual(eventsComponentAdded.count, 2)
|
||||
XCTAssertEqual(eventsEntityCreated.count, 1)
|
||||
|
||||
|
||||
family.createMember(with: (MyComponent(name: "Hello", flag: false), YourComponent(number: 05050)))
|
||||
XCTAssertEqual(family.count, 2)
|
||||
XCTAssertEqual(eventsMemberAdded.count, 2)
|
||||
XCTAssertEqual(eventsComponentAdded.count, 4)
|
||||
XCTAssertEqual(eventsEntityCreated.count, 2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate class DelegateTester: NexusEventDelegate {
|
||||
var onEvent: (NexusEvent) -> ()
|
||||
var onNonFatal: (String) -> ()
|
||||
|
||||
init(onEvent: @escaping (NexusEvent) -> Void = { _ in },
|
||||
onNonFatal: @escaping (String) -> Void = { _ in }) {
|
||||
self.onEvent = onEvent
|
||||
self.onNonFatal = onNonFatal
|
||||
}
|
||||
|
||||
func nexusEvent(_ event: NexusEvent) {
|
||||
onEvent(event)
|
||||
}
|
||||
|
||||
func nexusNonFatalError(_ message: String) {
|
||||
onNonFatal(message)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,436 +0,0 @@
|
|||
#if !canImport(ObjectiveC)
|
||||
import XCTest
|
||||
|
||||
extension ComponentIdentifierTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentIdentifierTests = [
|
||||
("testMirrorAsStableIdentifier", testMirrorAsStableIdentifier),
|
||||
("testStringDescribingAsStableIdentifier", testStringDescribingAsStableIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension ComponentInstanceProviderTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentInstanceProviderTests = [
|
||||
("testProviderReturnsTheInstance", testProviderReturnsTheInstance),
|
||||
("testProvidersWithDifferentInstanceHaveDifferentIdentifier", testProvidersWithDifferentInstanceHaveDifferentIdentifier),
|
||||
("testProvidersWithSameInstanceHaveSameIdentifier", testProvidersWithSameInstanceHaveSameIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension ComponentSingletonProviderTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentSingletonProviderTests = [
|
||||
("testProviderReturnsAnInstanceOfType", testProviderReturnsAnInstanceOfType),
|
||||
("testProviderReturnsSameInstanceEachTime", testProviderReturnsSameInstanceEachTime),
|
||||
("testProvidersWithDifferentTypeHaveDifferentIdentifier", testProvidersWithDifferentTypeHaveDifferentIdentifier),
|
||||
("testProvidersWithSameTypeHaveDifferentIdentifier", testProvidersWithSameTypeHaveDifferentIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension ComponentTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentTests = [
|
||||
("testComponentIdentifier", testComponentIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension ComponentTypeProviderTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__ComponentTypeProviderTests = [
|
||||
("testProviderReturnsAnInstanceOfType", testProviderReturnsAnInstanceOfType),
|
||||
("testProviderReturnsNewInstanceEachTime", testProviderReturnsNewInstanceEachTime),
|
||||
("testProvidersWithDifferentTypeHaveDifferentIdentifier", testProvidersWithDifferentTypeHaveDifferentIdentifier),
|
||||
("testProvidersWithSameTypeHaveSameIdentifier", testProvidersWithSameTypeHaveSameIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension DynamicComponentProviderTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__DynamicComponentProviderTests = [
|
||||
("testProviderReturnsTheInstance", testProviderReturnsTheInstance),
|
||||
("testProvidersWithDifferentMethodsHaveDifferentIdentifier", testProvidersWithDifferentMethodsHaveDifferentIdentifier),
|
||||
("testProvidersWithSameMethodHaveSameIdentifier", testProvidersWithSameMethodHaveSameIdentifier)
|
||||
]
|
||||
}
|
||||
|
||||
extension EntityCreationTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__EntityCreationTests = [
|
||||
("testBulkCreateEntitiesMultipleComponents", testBulkCreateEntitiesMultipleComponents),
|
||||
("testBulkCreateEntitiesOneComponent", testBulkCreateEntitiesOneComponent),
|
||||
("testCreateEntityMultipleComponents", testCreateEntityMultipleComponents),
|
||||
("testCreateEntityOneComponent", testCreateEntityOneComponent)
|
||||
]
|
||||
}
|
||||
|
||||
extension EntityIdGenTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__EntityIdGenTests = [
|
||||
("testGenerateWithInitialIds", testGenerateWithInitialIds),
|
||||
("testGeneratorDefaultInit", testGeneratorDefaultInit),
|
||||
("testGeneratorMarkUnused", testGeneratorMarkUnused),
|
||||
("testGeneratorWithDefaultEmptyCollection", testGeneratorWithDefaultEmptyCollection),
|
||||
("testLinearIncrement", testLinearIncrement)
|
||||
]
|
||||
}
|
||||
|
||||
extension EntityStateMachineTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__EntityStateMachineTests = [
|
||||
("testCallChangeStateWithSameNameLeavesEntityComponentsIntact", testCallChangeStateWithSameNameLeavesEntityComponentsIntact),
|
||||
("testCreateStateAddsState", testCreateStateAddsState),
|
||||
("testCreateStateDoesNotChangeState", testCreateStateDoesNotChangeState),
|
||||
("testEnterSecondStateAddsSecondStatesComponents", testEnterSecondStateAddsSecondStatesComponents),
|
||||
("testEnterSecondStateDoesNotRemoveOverlappingComponents", testEnterSecondStateDoesNotRemoveOverlappingComponents),
|
||||
("testEnterSecondStateRemovesDifferentComponentsOfSameType", testEnterSecondStateRemovesDifferentComponentsOfSameType),
|
||||
("testEnterSecondStateRemovesFirstStatesComponents", testEnterSecondStateRemovesFirstStatesComponents),
|
||||
("testEnterStateAddsStatesComponents", testEnterStateAddsStatesComponents),
|
||||
("testGetsDeinitedWhileBeingStronglyReferencedByComponentAssignedToEntity", testGetsDeinitedWhileBeingStronglyReferencedByComponentAssignedToEntity)
|
||||
]
|
||||
}
|
||||
|
||||
extension EntityStateTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__EntityStateTests = [
|
||||
("testAddInstanceCreatesMappingAndSetsInstanceProviderForInstanceType", testAddInstanceCreatesMappingAndSetsInstanceProviderForInstanceType),
|
||||
("testAddMappingWithInstanceQualifierCreatesInstanceProvider", testAddMappingWithInstanceQualifierCreatesInstanceProvider),
|
||||
("testAddMappingWithMethodQualifierCreatesDynamicProvider", testAddMappingWithMethodQualifierCreatesDynamicProvider),
|
||||
("testAddMappingWithNoQualifierCreatesTypeProvider", testAddMappingWithNoQualifierCreatesTypeProvider),
|
||||
("testAddMappingWithSingletonQualifierCreatesSingletonProvider", testAddMappingWithSingletonQualifierCreatesSingletonProvider),
|
||||
("testAddMappingWithTypeQualifierCreatesTypeProvider", testAddMappingWithTypeQualifierCreatesTypeProvider),
|
||||
("testAddMethodCreatesMappingAndSetsDynamicProviderForType", testAddMethodCreatesMappingAndSetsDynamicProviderForType),
|
||||
("testAddProviderCreatesMappingAndSetsProvider", testAddProviderCreatesMappingAndSetsProvider),
|
||||
("testAddSingletonCreatesMappingAndSetsSingletonProviderForType", testAddSingletonCreatesMappingAndSetsSingletonProviderForType),
|
||||
("testAddTypeCreatesMappingAndSetsTypeProviderForType", testAddTypeCreatesMappingAndSetsTypeProviderForType),
|
||||
("testHasProviderReturnsFalseForNotCreatedProvider", testHasProviderReturnsFalseForNotCreatedProvider),
|
||||
("testHasProviderReturnsTrueForCreatedProvider", testHasProviderReturnsTrueForCreatedProvider),
|
||||
("testProviderForTypeReturnsDynamicProvider", testProviderForTypeReturnsDynamicProvider),
|
||||
("testProviderForTypeReturnsInstanceProvider", testProviderForTypeReturnsInstanceProvider),
|
||||
("testProviderForTypeReturnsPassedProvider", testProviderForTypeReturnsPassedProvider),
|
||||
("testProviderForTypeReturnsSingletonProvider", testProviderForTypeReturnsSingletonProvider),
|
||||
("testProviderForTypeReturnsTypeProvider", testProviderForTypeReturnsTypeProvider),
|
||||
("testProviderForTypeReturnsTypeProviderByDefault", testProviderForTypeReturnsTypeProviderByDefault)
|
||||
]
|
||||
}
|
||||
|
||||
extension EntityTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__EntityTests = [
|
||||
("testAllComponentsOfEntity", testAllComponentsOfEntity),
|
||||
("testEntityEquality", testEntityEquality),
|
||||
("testEntityIdentifierAndIndex", testEntityIdentifierAndIndex),
|
||||
("testEntityIdGenerator", testEntityIdGenerator),
|
||||
("testEntitySubscripts", testEntitySubscripts),
|
||||
("testRemoveAllComponentsFromEntity", testRemoveAllComponentsFromEntity)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family1Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family1Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family2Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family2Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family3Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family3Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family4Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family4Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family5Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family5Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family6Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family6Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family7Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family7Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension Family8Tests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__Family8Tests = [
|
||||
("testComponentIteration", testComponentIteration),
|
||||
("testEntityComponentIteration", testEntityComponentIteration),
|
||||
("testEntityIteration", testEntityIteration),
|
||||
("testFamilyDecoding", testFamilyDecoding),
|
||||
("testFamilyEncoding", testFamilyEncoding),
|
||||
("testFamilyFailDecoding", testFamilyFailDecoding),
|
||||
("testMemberCreation", testMemberCreation),
|
||||
("testMemberCreationBuilder", testMemberCreationBuilder)
|
||||
]
|
||||
}
|
||||
|
||||
extension FamilyCodingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__FamilyCodingTests = [
|
||||
("testDecodeFamily4", testDecodeFamily4),
|
||||
("testDecodeFamily5", testDecodeFamily5),
|
||||
("testDecodingFamily1", testDecodingFamily1),
|
||||
("testDecodingFamily2", testDecodingFamily2),
|
||||
("testDecodingFamily3", testDecodingFamily3),
|
||||
("testEncodeFamily2", testEncodeFamily2),
|
||||
("testEncodeFamily3", testEncodeFamily3),
|
||||
("testEncodeFamily4", testEncodeFamily4),
|
||||
("testEncodeFamily5", testEncodeFamily5),
|
||||
("testEncodingFamily1", testEncodingFamily1),
|
||||
("testFailDecodingFamily", testFailDecodingFamily)
|
||||
]
|
||||
}
|
||||
|
||||
extension FamilyTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__FamilyTests = [
|
||||
("testFamilyAbandoned", testFamilyAbandoned),
|
||||
("testFamilyBulkDestroy", testFamilyBulkDestroy),
|
||||
("testFamilyCreateMembers", testFamilyCreateMembers),
|
||||
("testFamilyCreation", testFamilyCreation),
|
||||
("testFamilyDestroyMembers", testFamilyDestroyMembers),
|
||||
("testFamilyExchange", testFamilyExchange),
|
||||
("testFamilyLateMember", testFamilyLateMember),
|
||||
("testFamilyMemberBasicIteration", testFamilyMemberBasicIteration),
|
||||
("testFamilyReuse", testFamilyReuse)
|
||||
]
|
||||
}
|
||||
|
||||
extension FamilyTraitsTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__FamilyTraitsTests = [
|
||||
("testTraitCommutativity", testTraitCommutativity),
|
||||
("testTraitMatching", testTraitMatching)
|
||||
]
|
||||
}
|
||||
|
||||
extension HashingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__HashingTests = [
|
||||
("testCollisionsInCritialRange", testCollisionsInCritialRange),
|
||||
("testStringHashes", testStringHashes)
|
||||
]
|
||||
}
|
||||
|
||||
extension NexusTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__NexusTests = [
|
||||
("testComponentCreation", testComponentCreation),
|
||||
("testComponentDeletion", testComponentDeletion),
|
||||
("testComponentRetrieval", testComponentRetrieval),
|
||||
("testComponentUniqueness", testComponentUniqueness),
|
||||
("testEntityCreate", testEntityCreate),
|
||||
("testEntityDestroy", testEntityDestroy),
|
||||
("testEntityIteration", testEntityIteration)
|
||||
]
|
||||
}
|
||||
|
||||
extension SingleTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SingleTests = [
|
||||
("testSingleCreation", testSingleCreation),
|
||||
("testSingleCreationOnExistingFamilyMember", testSingleCreationOnExistingFamilyMember),
|
||||
("testSingleEntityAndComponentCreation", testSingleEntityAndComponentCreation),
|
||||
("testSingleReuse", testSingleReuse)
|
||||
]
|
||||
}
|
||||
|
||||
extension SparseSetTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SparseSetTests = [
|
||||
("testAlternativeKey", testAlternativeKey),
|
||||
("testEquality", testEquality),
|
||||
("testSparseSetAdd", testSparseSetAdd),
|
||||
("testSparseSetAddAndReplace", testSparseSetAddAndReplace),
|
||||
("testSparseSetClear", testSparseSetClear),
|
||||
("testSparseSetDoubleRemove", testSparseSetDoubleRemove),
|
||||
("testSparseSetGet", testSparseSetGet),
|
||||
("testSparseSetNonCongiuousData", testSparseSetNonCongiuousData),
|
||||
("testSparseSetReduce", testSparseSetReduce),
|
||||
("testSparseSetRemove", testSparseSetRemove),
|
||||
("testSparseSetRemoveAndAdd", testSparseSetRemoveAndAdd),
|
||||
("testSparseSetRemoveNonPresent", testSparseSetRemoveNonPresent),
|
||||
("testStartEndIndex", testStartEndIndex),
|
||||
("testSubscript", testSubscript)
|
||||
]
|
||||
}
|
||||
|
||||
extension StateComponentMappingTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__StateComponentMappingTests = [
|
||||
("testAddAddsProviderToState", testAddAddsProviderToState),
|
||||
("testAddReturnsSameMappingForDifferentComponentTypes", testAddReturnsSameMappingForDifferentComponentTypes),
|
||||
("testAddReturnsSameMappingForSameComponentType", testAddReturnsSameMappingForSameComponentType)
|
||||
]
|
||||
}
|
||||
|
||||
extension SystemsTests {
|
||||
// DO NOT MODIFY: This is autogenerated, use:
|
||||
// `swift test --generate-linuxmain`
|
||||
// to regenerate.
|
||||
static let __allTests__SystemsTests = [
|
||||
("testSystemsUpdate", testSystemsUpdate)
|
||||
]
|
||||
}
|
||||
|
||||
public func __allTests() -> [XCTestCaseEntry] {
|
||||
return [
|
||||
testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests),
|
||||
testCase(ComponentInstanceProviderTests.__allTests__ComponentInstanceProviderTests),
|
||||
testCase(ComponentSingletonProviderTests.__allTests__ComponentSingletonProviderTests),
|
||||
testCase(ComponentTests.__allTests__ComponentTests),
|
||||
testCase(ComponentTypeProviderTests.__allTests__ComponentTypeProviderTests),
|
||||
testCase(DynamicComponentProviderTests.__allTests__DynamicComponentProviderTests),
|
||||
testCase(EntityCreationTests.__allTests__EntityCreationTests),
|
||||
testCase(EntityIdGenTests.__allTests__EntityIdGenTests),
|
||||
testCase(EntityStateMachineTests.__allTests__EntityStateMachineTests),
|
||||
testCase(EntityStateTests.__allTests__EntityStateTests),
|
||||
testCase(EntityTests.__allTests__EntityTests),
|
||||
testCase(Family1Tests.__allTests__Family1Tests),
|
||||
testCase(Family2Tests.__allTests__Family2Tests),
|
||||
testCase(Family3Tests.__allTests__Family3Tests),
|
||||
testCase(Family4Tests.__allTests__Family4Tests),
|
||||
testCase(Family5Tests.__allTests__Family5Tests),
|
||||
testCase(Family6Tests.__allTests__Family6Tests),
|
||||
testCase(Family7Tests.__allTests__Family7Tests),
|
||||
testCase(Family8Tests.__allTests__Family8Tests),
|
||||
testCase(FamilyCodingTests.__allTests__FamilyCodingTests),
|
||||
testCase(FamilyTests.__allTests__FamilyTests),
|
||||
testCase(FamilyTraitsTests.__allTests__FamilyTraitsTests),
|
||||
testCase(HashingTests.__allTests__HashingTests),
|
||||
testCase(NexusTests.__allTests__NexusTests),
|
||||
testCase(SingleTests.__allTests__SingleTests),
|
||||
testCase(SparseSetTests.__allTests__SparseSetTests),
|
||||
testCase(StateComponentMappingTests.__allTests__StateComponentMappingTests),
|
||||
testCase(SystemsTests.__allTests__SystemsTests)
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import XCTest
|
||||
|
||||
import FirebladeECSPerformanceTests
|
||||
import FirebladeECSTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += FirebladeECSPerformanceTests.__allTests()
|
||||
tests += FirebladeECSTests.__allTests()
|
||||
|
||||
XCTMain(tests)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
":combinePatchMinorReleases",
|
||||
":rebaseStalePrs",
|
||||
":renovatePrefix",
|
||||
"customManagers:githubActionsVersions"
|
||||
],
|
||||
"prConcurrentLimit": 5,
|
||||
"prHourlyLimit": 1,
|
||||
"recreateWhen": "auto",
|
||||
"prCreation": "not-pending",
|
||||
"packageRules": [
|
||||
{
|
||||
"groupName": "Swift non-major dependencies",
|
||||
"description": "Group all Swift package minor and patch updates together",
|
||||
"matchManagers": [
|
||||
"swift"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"labels": [
|
||||
"dependencies",
|
||||
"renovate",
|
||||
"swift"
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupName": "GitHub Actions non-major dependencies",
|
||||
"description": "Group all GitHub Actions minor and patch updates together",
|
||||
"matchManagers": [
|
||||
"github-actions"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"labels": [
|
||||
"dependencies",
|
||||
"renovate",
|
||||
"ci"
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupName": "Mint non-major dependencies",
|
||||
"description": "Group all Mint package minor and patch updates together",
|
||||
"matchManagers": [
|
||||
"mint"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"labels": [
|
||||
"dependencies",
|
||||
"renovate",
|
||||
"mint"
|
||||
]
|
||||
}
|
||||
],
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Update swift-tools-version in Package.swift",
|
||||
"fileMatch": [
|
||||
"Package\\.swift"
|
||||
],
|
||||
"matchStrings": [
|
||||
"^\/\/(\\s)?swift-tools-version:[\\s]?(?<currentValue>[0-9.]+)"
|
||||
],
|
||||
"datasourceTemplate": "github-releases",
|
||||
"depNameTemplate": "swiftlang/swift",
|
||||
"extractVersionTemplate": "^swift-(?<version>.*)-RELEASE$"
|
||||
},
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Update .swift-version",
|
||||
"fileMatch": ".swift-version",
|
||||
"matchStrings": [
|
||||
"(?<currentValue>[0-9.]+)"
|
||||
],
|
||||
"datasourceTemplate": "github-releases",
|
||||
"depNameTemplate": "swiftlang/swift",
|
||||
"extractVersionTemplate": "^swift-(?<version>.*)-RELEASE$"
|
||||
}
|
||||
],
|
||||
"labels": [
|
||||
"dependencies",
|
||||
"renovate"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue