Compare commits

..

No commits in common. "master" and "0.17.4" have entirely different histories.

61 changed files with 878 additions and 1632 deletions

View File

@ -1,6 +1,5 @@
ignore:
- "Tests/"
- "Benchmarks/"
comment:
layout: header, changes, diff
layout: header, changes, diff

2
.github/FUNDING.yml vendored
View File

@ -1,2 +0,0 @@
github: [ctreffs]
custom: ['https://www.paypal.com/donate?hosted_button_id=GCG3K54SKRALQ']

View File

@ -1,61 +0,0 @@
---
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.*

View File

@ -1,31 +0,0 @@
---
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.*

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Discord
url: https://discord.gg/JMM7W6pCCc
about: Questions or comments about using Fireblade? Ask here!

View File

@ -1,52 +0,0 @@
<!--
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).

View File

@ -1,39 +0,0 @@
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

View File

@ -1,62 +0,0 @@
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

View File

@ -1,16 +0,0 @@
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

View File

@ -1,47 +0,0 @@
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

81
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,81 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
macOS:
runs-on: macOS-latest
strategy:
matrix:
xcode: ["11.7", "12.2"]
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@main
with:
shell-action: swift build --triple wasm32-unknown-wasi

View File

@ -1,76 +1,32 @@
# 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
branches: [ master ]
paths:
- .github/workflows/documentation.yml
- Sources/FirebladeECS/**.swift
workflow_dispatch:
concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true
branches: [ master ]
paths:
- .github/workflows/documentation.yml
- Sources/FirebladeECS/**.swift
jobs:
documentation:
runs-on: macos-15
build:
runs-on: ubuntu-latest
steps:
- name: Select Xcode 16.0
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: Checkout Package
uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Generate documentation
uses: SwiftDocOrg/swift-doc@master
with:
fetch-depth: 0
- name: Checkout gh-pages Branch
uses: actions/checkout@v4
inputs: "Sources/FirebladeECS"
output: "Documentation"
- name: Publish to wiki
uses: SwiftDocOrg/github-wiki-publish-action@master
with:
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
path: "Documentation"
env:
GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@master
- name: markdown-link-check
uses: gaurav-nelson/github-action-markdown-link-check@1.0.16
uses: gaurav-nelson/github-action-markdown-link-check@master

25
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,25 @@
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

View File

@ -1,4 +0,0 @@
version: 1
builder:
configs:
- documentation_targets: [FirebladeECS]

View File

@ -1 +1 @@
5.8
5.1.3

View File

@ -1,13 +0,0 @@
# 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

View File

@ -9,11 +9,23 @@ 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
@ -36,6 +48,7 @@ opt_in_rules:
- enum_case_associated_values_count
- expiring_todo
- explicit_init
- explicit_self
- explicit_top_level_acl
- fallthrough
- fatal_error_message
@ -96,6 +109,8 @@ 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

View File

@ -1,85 +0,0 @@
//
// 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
}

View File

@ -1,198 +0,0 @@
// 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
}
)
}
}
}

View File

@ -1,28 +0,0 @@
// 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")
]
)
]
)

View File

@ -1,22 +0,0 @@
# 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).

View File

@ -1,12 +0,0 @@
# 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

View File

@ -1,136 +0,0 @@
# 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/

View File

@ -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/courses/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/series/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__][ref-issues-new] and [__enhancement__][ref-pull-request-new] or
[__writing code__][ref-pull-request-new] 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__ and __feature requests__ or
__writing code__ which can be incorporated into the repository itself.
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.
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.
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,12 +55,11 @@ 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, if any?
- What alternatives where considered?
- Has this feature impact on performance or stability of the project?
#### Your contribution responsibilities
@ -69,7 +68,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. Project wide commands are available through the [Makefile][ref-makefile] in the repository root.
- [x] Ensure **coding conventions** are met. Lint your code with the project's default tools.
- [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.
@ -81,7 +80,7 @@ Please consult the [README][ref-readme] for installation instructions.
<!-- REFERENCES -->
[ref-code-of-conduct]: https://github.com/fireblade-engine/ecs/blob/master/CODE_OF_CONDUCT.md
[ref-code-of-conduct]: t.b.d.
[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
@ -92,4 +91,3 @@ 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

134
Makefile
View File

@ -1,95 +1,79 @@
SWIFT_PACKAGE_VERSION := $(shell swift package tools-version)
# Version 1.0.0
UNAME_S := $(shell uname -s)
# 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
# Lint
lint:
swiftlint autocorrect --format
swiftlint lint --quiet
# Generate code
.PHONY: generate-code
generate-code:
mint run sourcery --quiet --config ./.sourcery.yml
mint run sourcery --quiet --config ./.sourceryTests.yml
lintErrorOnly:
@swiftlint autocorrect --format --quiet
@swiftlint lint --quiet | grep error
# Run pre-push tasks
.PHONY: pre-push
pre-push: generate-code lint-fix
# Git
precommit: generateCode generateTestsCode lint genLinuxTests
.PHONY: precommit
precommit: pre-push
submodule:
git submodule init
git submodule update --recursive
.PHONY: setup-brew
setup-brew:
@which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
@brew update
# Tests
genLinuxTests:
swift test --generate-linuxmain
swiftlint autocorrect --format --path Tests/
.PHONY: install-dependencies-macOS
install-dependencies-macOS: setup-brew
brew install mint
mint bootstrap
test: genLinuxTests
swift test
.PHONY: setupEnvironment
setupEnvironment: install-dependencies-macOS
# Package
latest:
swift package update
# Build debug version
.PHONY: build-debug
build-debug:
swift build -c debug
resolve:
swift package resolve
# Build release version
.PHONY: build-release
build-release:
swift build -c release --skip-update
# 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
# Test links in README
# requires <https://github.com/tcort/markdown-link-check>
.PHONY: testReadme
testReadme:
markdown-link-check -p -v ./README.md
# Delete package build artifacts.
.PHONY: clean
clean: clean-sourcery
swift package clean
generateCode:
sourcery --config ./.sourcery.yml --verbose
generateTestsCode:
sourcery --config ./.sourceryTests.yml --verbose
# Clean sourcery cache
.PHONY: clean-sourcery
clean-sourcery:
rm -rdf ${HOME}/Library/Caches/Sourcery
brewInstallDeps: brewUpdate
brew install swiftenv
brew install swiftlint
brew install sourcery
# Preview DocC documentation
.PHONY: preview-docs
preview-docs:
swift package --disable-sandbox preview-documentation --target FirebladeECS
brewSetup:
which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
# 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
brewUpdate: brewSetup
brew update
# 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 \
setupEnvironment: brewInstallDeps
open Package.swift
# 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'
# lines of code
loc: clean
find . -name "*.swift" -print0 | xargs -0 wc -l

View File

@ -1,3 +0,0 @@
realm/SwiftLint@0.57.0
nicklockwood/SwiftFormat@0.55.0
krzysztofzablocki/Sourcery@2.2.5

View File

@ -1,23 +0,0 @@
{
"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
}

View File

@ -1,4 +1,4 @@
// swift-tools-version:5.8
// swift-tools-version:5.1
import PackageDescription
let package = Package(
@ -7,15 +7,10 @@ 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",
exclude: ["Stencils/Family.stencil"]),
.target(name: "FirebladeECS"),
.testTarget(name: "FirebladeECSTests",
dependencies: ["FirebladeECS"],
exclude: ["Stencils/FamilyTests.stencil"]),
dependencies: ["FirebladeECS"]),
.testTarget(name: "FirebladeECSPerformanceTests",
dependencies: ["FirebladeECS"])
],

View File

@ -1,13 +1,11 @@
# Fireblade ECS (Entity-Component System)
[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
[![macOS](https://github.com/fireblade-engine/ecs/actions/workflows/ci-macos.yml/badge.svg)](https://github.com/fireblade-engine/ecs/actions/workflows/ci-macos.yml)
[![Linux](https://github.com/fireblade-engine/ecs/actions/workflows/ci-linux.yml/badge.svg)](https://github.com/fireblade-engine/ecs/actions/workflows/ci-linux.yml)
[![Windows](https://github.com/fireblade-engine/ecs/actions/workflows/ci-windows.yml/badge.svg)](https://github.com/fireblade-engine/ecs/actions/workflows/ci-windows.yml)
[![WASM](https://github.com/fireblade-engine/ecs/actions/workflows/ci-wasm.yml/badge.svg)](https://github.com/fireblade-engine/ecs/actions/workflows/ci-wasm.yml)
[![documentation](https://github.com/fireblade-engine/ecs/workflows/Documentation/badge.svg)](https://github.com/fireblade-engine/ecs/wiki)
[![github CI](https://github.com/fireblade-engine/ecs/workflows/CI/badge.svg)](https://github.com/fireblade-engine/ecs/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/fireblade-engine/ecs/branch/master/graph/badge.svg)](https://codecov.io/gh/fireblade-engine/ecs)
[![documentation](https://github.com/fireblade-engine/ecs/workflows/Documentation/badge.svg)](https://github.com/fireblade-engine/ecs/wiki)
[![spi-swift-versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffireblade-engine%2Fecs%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/fireblade-engine/ecs)
[![spi-swift-platforms](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffireblade-engine%2Fecs%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/fireblade-engine/ecs)
[![platform-webassembly](https://img.shields.io/badge/Platform-WebAssembly-blue.svg)](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).
@ -25,19 +23,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.8](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.1](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.8
// swift-tools-version:5.1
import PackageDescription
let package = Package(
name: "YourPackageName",
dependencies: [
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.5")
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.4")
],
targets: [
.target(
@ -233,9 +231,8 @@ See the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo) t
## 📖 Documentation
Consult the [online documentation](https://swiftpackageindex.com/fireblade-engine/ecs/documentation/FirebladeECS), or preview it locally:
Consult the [wiki](https://github.com/fireblade-engine/ecs/wiki) for in-depth [documentation](https://github.com/fireblade-engine/ecs/wiki).
- `make preview-docs`
## 💁 How to contribute

View File

@ -13,6 +13,6 @@ public struct DynamicCodingKey: CodingKey {
public var intValue: Int?
public var stringValue: String
public init?(intValue: Int) { self.intValue = intValue; stringValue = "\(intValue)" }
public init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
public init?(stringValue: String) { self.stringValue = stringValue }
}

View File

@ -13,15 +13,15 @@ public struct ComponentIdentifier {
extension ComponentIdentifier {
@usableFromInline
init(_ componentType: (some Component).Type) {
id = Self.makeRuntimeHash(componentType)
init<C>(_ componentType: C.Type) where C: Component {
self.id = Self.makeRuntimeHash(componentType)
}
/// object identifier hash (only stable during runtime) - arbitrary hash is ok.
static func makeRuntimeHash(_ componentType: (some Component).Type) -> Identifier {
internal static func makeRuntimeHash<C>(_ componentType: C.Type) -> Identifier where C: Component {
ObjectIdentifier(componentType).hashValue
}
}
extension ComponentIdentifier: Equatable {}
extension ComponentIdentifier: Hashable {}
extension ComponentIdentifier: Equatable { }
extension ComponentIdentifier: Hashable { }

View File

@ -1,112 +0,0 @@
# ``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``

View File

@ -1,210 +0,0 @@
# 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)
```

View File

@ -17,9 +17,9 @@ public struct Entity {
/// The unique entity identifier.
public private(set) var identifier: EntityIdentifier
init(nexus: Nexus, id: EntityIdentifier) {
internal init(nexus: Nexus, id: EntityIdentifier) {
self.nexus = nexus
identifier = id
self.identifier = id
}
/// Returns the number of components for this entity.
@ -38,13 +38,13 @@ public struct Entity {
}
@discardableResult
public func createEntity(with components: some Collection<Component>) -> Entity {
public func createEntity<C>(with components: C) -> Entity where C: Collection, C.Element == Component {
nexus.createEntity(with: components)
}
/// Checks if a component with given type is assigned to this entity.
/// - Parameter type: the component type.
public func has(_ type: (some Component).Type) -> Bool {
public func has<C>(_ type: C.Type) -> Bool where C: Component {
has(type.identifier)
}
@ -78,13 +78,13 @@ public struct Entity {
/// Add a typed component to this entity.
/// - Parameter component: the typed component.
@discardableResult
public func assign(_ component: some Component) -> Entity {
public func assign<C>(_ component: C) -> Entity where C: Component {
assign(component)
return self
}
@discardableResult
public func assign(_ components: some Collection<Component>) -> Entity {
public func assign<C>(_ components: C) -> Entity where C: Collection, C.Element == Component {
nexus.assign(components: components, to: self)
return self
}
@ -92,14 +92,14 @@ public struct Entity {
/// Remove a component from this entity.
/// - Parameter component: the component.
@discardableResult
public func remove(_ component: some Component) -> Entity {
public func remove<C>(_ component: C) -> Entity where C: Component {
remove(component.identifier)
}
/// Remove a component by type from this entity.
/// - Parameter compType: the component type.
@discardableResult
public func remove(_ compType: (some Component).Type) -> Entity {
public func remove<C>(_ compType: C.Type) -> Entity where C: Component {
remove(compType.identifier)
}
@ -130,7 +130,7 @@ public struct Entity {
extension Entity {
public struct ComponentsIterator: IteratorProtocol {
private var iterator: IndexingIterator<[Component]>?
private var iterator: IndexingIterator<([Component])>?
@usableFromInline
init(nexus: Nexus, entityIdentifier: EntityIdentifier) {
@ -144,9 +144,8 @@ extension Entity {
}
}
}
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 {

View File

@ -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) {
id = rawValue
self.id = rawValue
}
}

View File

@ -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(\.id)
let initialInUse: [EntityIdentifier.Identifier] = initialEntityIds.map { $0.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 {
storage = Storage(startProviding: initialEntityIds)
self.storage = Storage(startProviding: initialEntityIds)
}
@inlinable
public init() {
storage = Storage()
self.storage = Storage()
}
@inline(__always)

View File

@ -97,7 +97,9 @@ 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
@ -169,7 +171,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 {
var providers = [ComponentIdentifier: ComponentProvider]()
internal var providers = [ComponentIdentifier: ComponentProvider]()
public init() {}
@ -251,7 +253,7 @@ extension EntityState {
/// - Returns: This EntityState, so more modifications can be applied.
@inline(__always)
@discardableResult
public func addProvider(type: (some ComponentInitializable).Type, provider: ComponentProvider) -> Self {
public func addProvider<C: ComponentInitializable>(type: C.Type, provider: ComponentProvider) -> Self {
addMapping(for: type).withProvider(provider)
return self
}
@ -270,7 +272,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
init(creatingState: EntityState, type: ComponentInitializable.Type) {
internal init(creatingState: EntityState, type: ComponentInitializable.Type) {
self.creatingState = creatingState
componentType = type
provider = ComponentTypeProvider(type: type)
@ -316,7 +318,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(_ closure: DynamicComponentProvider<some Component>.Closure) -> Self {
public func withMethod<C: Component>(_ closure: DynamicComponentProvider<C>.Closure) -> Self {
setProvider(DynamicComponentProvider(closure: closure))
return self
}
@ -401,7 +403,7 @@ public class EntityStateMachine<StateIdentifier: Hashable> {
var toAdd: [ComponentIdentifier: ComponentProvider]
if let currentState {
if let currentState = currentState {
toAdd = .init()
for (identifier, provider) in newState.providers {
toAdd[identifier] = provider
@ -409,8 +411,7 @@ 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)

View File

@ -14,7 +14,6 @@ extension CodingUserInfoKey {
}
// MARK: - encoding
extension FamilyMemberContainer: Encodable where R: FamilyEncoding {
func encode(to encoder: Encoder) throws {
let strategy = encoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy()
@ -51,12 +50,11 @@ 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()
components = try R.decode(componentsIn: &familyContainer, using: strategy)
self.components = try R.decode(componentsIn: &familyContainer, using: strategy)
}
}

View File

@ -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 {
createMember(with: builder())
self.createMember(with: builder())
}
}
@ -70,17 +70,16 @@ 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>) {
nexus = family.nexus
self.nexus = family.nexus
memberIdsIterator = family.memberIds.makeIterator()
}
@ -94,11 +93,10 @@ 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)
@ -109,7 +107,7 @@ extension Family {
@usableFromInline unowned let nexus: Nexus
public init(family: Family<R>) {
nexus = family.nexus
self.nexus = family.nexus
memberIdsIterator = family.memberIds.makeIterator()
}
@ -122,11 +120,10 @@ 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)
@ -137,7 +134,7 @@ extension Family {
@usableFromInline unowned let nexus: Nexus
public init(family: Family<R>) {
nexus = family.nexus
self.nexus = family.nexus
memberIdsIterator = family.memberIds.makeIterator()
}
@ -150,11 +147,10 @@ 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.
///

View File

@ -5,10 +5,6 @@
// Created by Christian Treffs on 07.08.20.
//
#if swift(<5.4)
@_functionBuilder
public enum FamilyMemberBuilder<R> where R: FamilyRequirementsManaging {}
#else
@resultBuilder
public enum FamilyMemberBuilder<R> where R: FamilyRequirementsManaging {}
#endif
@_functionBuilder
public enum FamilyMemberBuilderPreview<R> where R: FamilyRequirementsManaging { }
public typealias FamilyMemberBuilder<R> = FamilyMemberBuilderPreview<R> where R: FamilyRequirementsManaging

View File

@ -19,7 +19,7 @@ public struct FamilyTraitSet {
self.requiresAll = requiresAll
self.excludesAll = excludesAll
setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
self.setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll])
}
@inlinable

View File

@ -6,8 +6,8 @@
//
#if canImport(Foundation)
import Foundation
import Foundation
extension JSONEncoder: TopLevelEncoder {}
extension JSONDecoder: TopLevelDecoder {}
extension JSONEncoder: TopLevelEncoder { }
extension JSONDecoder: TopLevelDecoder { }
#endif

View File

@ -1,5 +1,6 @@
// Generated using Sourcery 2.2.5 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 1.0.0 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
// swiftlint:disable file_length
// swiftlint:disable function_parameter_count
// swiftlint:disable large_tuple

View File

@ -6,11 +6,11 @@
//
#if arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) // 64 bit
private let kFibA: UInt = 0x9E37_79B9_7F4A_7C15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio)
private let kFibA: UInt = 0x9e3779b97f4a7c15 // = 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 = 0x9E37_79B9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio)
private let kFibA: UInt = 0x9e3779b9 // = 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,11 +20,8 @@ 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.
/// It produces 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.
/// Will always produce the same result for the same combination of seed and value during the single run of a program.
///
/// - Parameters:
/// - seed: seed hash.
@ -48,10 +45,8 @@ 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.
/// The hash value this method computes is sensitive to the order of the elements.
/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range.
/// 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 {
@ -60,49 +55,45 @@ public func hash<H: Sequence>(combine hashValues: H) -> Int where H.Element: Has
}
// MARK: - entity component hash
extension EntityComponentHash {
static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
internal 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)
}
static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
internal static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
let entityIdSwapped = UInt(entityId.id).byteSwapped
let entityIdSwappedInt = Int(bitPattern: entityIdSwapped)
return hash ^ entityIdSwappedInt
}
static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
internal static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
let entityId: Int = (hash ^ componentTypeHash).byteSwapped
return EntityIdentifier(UInt32(truncatingIfNeeded: entityId))
}
}
// MARK: - string hashing
/// 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)
/// <https://stackoverflow.com/a/52440609>
public enum StringHashing {
/// *Warren Stringer djb2*
/// *Waren Singer djb2*
///
/// Implementation from <https://stackoverflow.com/a/43149500>
/// <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 & 0xFF_FFFF_FFFF_FFFF) &+ UInt64(char.value)
hash = 127 * (hash & 0xFFFFFFFFFFFFFF) &+ 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>

View File

@ -4,7 +4,6 @@
//
// 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
@ -12,24 +11,16 @@ 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 {
@ -43,8 +34,6 @@ 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 {
@ -53,25 +42,16 @@ 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 {
@ -85,23 +65,17 @@ 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)
@ -109,8 +83,6 @@ 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)
@ -120,7 +92,6 @@ 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
@ -128,5 +99,4 @@ extension ManagedContiguousArray: Equatable where Element: Equatable {
}
// MARK: - Codable
extension ManagedContiguousArray: Codable where Element: Codable {}
extension ManagedContiguousArray: Codable where Element: Codable { }

View File

@ -28,12 +28,12 @@ extension Nexus {
}
@discardableResult
public final func assign(component: some Component, to entity: Entity) -> Bool {
public final func assign<C>(component: C, to entity: Entity) -> Bool where C: Component {
assign(component: component, to: entity)
}
@discardableResult
public final func assign(components: some Collection<Component>, to entity: Entity) -> Bool {
public final func assign<C>(components: C, to entity: Entity) -> Bool where C: Collection, C.Element == Component {
assign(components: components, to: entity.identifier)
}
@ -93,7 +93,7 @@ extension Nexus {
return false
}
var iter = allComponents.makeIterator()
var removedAll = true
var removedAll: Bool = true
while let component = iter.next() {
removedAll = removedAll && remove(component: component, from: entityId)
}

View File

@ -5,13 +5,9 @@
// Created by Christian Treffs on 30.07.20.
//
#if swift(<5.4)
@_functionBuilder
public enum ComponentsBuilder {}
#else
@resultBuilder
public enum ComponentsBuilder {}
#endif
@_functionBuilder
public enum ComponentsBuilderPreview { }
public typealias ComponentsBuilder = ComponentsBuilderPreview
extension ComponentsBuilder {
public static func buildBlock(_ components: Component...) -> [Component] {
@ -39,7 +35,7 @@ extension Nexus {
/// - Returns: The newly created entity with the provided component assigned.
@discardableResult
public func createEntity(@ComponentsBuilder using builder: () -> Component) -> Entity {
createEntity(with: builder())
self.createEntity(with: builder())
}
/// Create an entity assigning multiple components.
@ -55,7 +51,7 @@ extension Nexus {
/// - Returns: The newly created entity with the provided components assigned.
@discardableResult
public func createEntity(@ComponentsBuilder using builder: () -> [Component]) -> Entity {
createEntity(with: builder())
self.createEntity(with: builder())
}
/// Create multiple entities assigning one component each.
@ -72,7 +68,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.
@ -90,6 +86,6 @@ extension Nexus {
/// - Returns: The newly created entities with the provided components assigned.
@discardableResult
public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> [Component] = { _ in [] }) -> [Entity] {
(0 ..< count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
(0..<count).map { self.createEntity(with: builder(ComponentsBuilder.Context(index: $0))) }
}
}

View File

@ -22,8 +22,8 @@ extension Nexus {
}
@discardableResult
public func createEntity(with components: some Collection<Component>) -> Entity {
let entity = createEntity()
public func createEntity<C>(with components: C) -> Entity where C: Collection, C.Element == Component {
let entity = self.createEntity()
assign(components: components, to: entity.identifier)
return entity
}
@ -50,7 +50,7 @@ extension Nexus {
@discardableResult
public func destroy(entity: Entity) -> Bool {
destroy(entityId: entity.identifier)
self.destroy(entityId: entity.identifier)
}
@discardableResult
@ -76,7 +76,6 @@ extension Nexus {
}
// MARK: - entities iterator
extension Nexus {
public struct EntitiesIterator: IteratorProtocol {
private var iterator: AnyIterator<Entity>
@ -97,6 +96,5 @@ extension Nexus {
}
}
}
extension Nexus.EntitiesIterator: LazySequenceProtocol {}
extension Nexus.EntitiesIterator: Sequence {}
extension Nexus.EntitiesIterator: LazySequenceProtocol { }
extension Nexus.EntitiesIterator: Sequence { }

View File

@ -8,7 +8,7 @@
extension Nexus {
@usableFromInline
@discardableResult
func assign(components: some Collection<Component>, to entityId: EntityIdentifier) -> Bool {
func assign<C>(components: C, to entityId: EntityIdentifier) -> Bool where C: Collection, C.Element == Component {
var iter = components.makeIterator()
while let component = iter.next() {
let componentId = component.identifier
@ -103,8 +103,8 @@ extension Nexus {
return
}
let isMember: Bool = isMember(entity: entityId, inFamilyWithTraits: traits)
if !exists(entity: entityId), isMember {
let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits)
if !exists(entity: entityId) && isMember {
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
return
}

View File

@ -42,14 +42,13 @@ public final class Nexus {
codingStrategy: DefaultCodingStrategy())
}
init(componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>],
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
entityIdGenerator: EntityIdentifierGenerator,
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Identifier>],
codingStrategy: CodingStrategy)
{
internal init(componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>],
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
entityIdGenerator: EntityIdentifierGenerator,
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Identifier>],
codingStrategy: CodingStrategy) {
self.componentsByType = componentsByType
componentIdsByEntity = componentsByEntity
self.componentIdsByEntity = componentsByEntity
self.familyMembersByTraits = familyMembersByTraits
self.entityIdGenerator = entityIdGenerator
self.codingStrategy = codingStrategy
@ -67,7 +66,6 @@ public final class Nexus {
}
// MARK: - CustomDebugStringConvertible
extension Nexus: CustomDebugStringConvertible {
public var debugDescription: String {
"<Nexus entities:\(numEntities) components:\(numComponents) families:\(numFamilies)>"
@ -75,9 +73,8 @@ 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

View File

@ -1,5 +1,5 @@
//
// NexusEvent.swift
// NexusEvents.swift
// FirebladeECS
//
// Created by Christian Treffs on 08.10.17.

View File

@ -32,13 +32,13 @@ extension Single where A: SingleComponent {
}
public var entity: Entity {
Entity(nexus: nexus, id: entityId)
Entity(nexus: self.nexus, id: entityId)
}
}
extension Nexus {
public func single<S>(_ component: S.Type) -> Single<S> where S: SingleComponent {
let family = family(requires: S.self)
let family = self.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 {

View File

@ -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 = dense[denseIndex]
let entry = self.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,7 +130,6 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
}
}
/// Creates a new sparse set.
public init() {
self.init(storage: Storage())
}
@ -142,26 +141,21 @@ 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)
@ -169,16 +163,13 @@ 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` if the key wasn't found.
/// - Parameter key: the key
/// - Returns: the element or nil of key not 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
@ -193,21 +184,17 @@ 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 {
@ -221,14 +208,12 @@ 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>>
@ -241,20 +226,17 @@ 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
@ -262,7 +244,6 @@ 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 { }

View File

@ -5,7 +5,6 @@
// Created by Christian Treffs on 14.02.19.
//
#if os(macOS)
import FirebladeECS
import XCTest
@ -98,6 +97,3 @@ class HashingPerformanceTests: XCTestCase {
#endif
}
}
#else
#warning("Skipping HashingPerformanceTests")
#endif

View File

@ -5,7 +5,6 @@
// Created by Christian Treffs on 05.10.19.
//
#if os(macOS)
import FirebladeECS
import XCTest
@ -31,7 +30,7 @@ final class TypeIdentifierPerformanceTests: XCTestCase {
/// release: 1.034 sec
/// debug:
func testPerformanceHash() {
measure(options: .default) {
measure {
for _ in 0..<maxIterations {
_ = StringHashing.singer_djb2(String(describing: Color.self))
_ = StringHashing.singer_djb2(String(describing: EmptyComponent.self))
@ -92,6 +91,3 @@ final class TypeIdentifierPerformanceTests: XCTestCase {
}
}
}
#else
#warning("Skipping TypeIdentifierPerformanceTests tests")
#endif

View File

@ -0,0 +1,70 @@
#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

View File

@ -1,5 +1,6 @@
// Generated using Sourcery 2.2.5 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 1.0.0 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
import FirebladeECS
import XCTest

View File

@ -0,0 +1,454 @@
#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),
("testComponentsIteration", testComponentsIteration),
("testEntityCreationIntrinsic", testEntityCreationIntrinsic),
("testEntityDescriptions", testEntityDescriptions),
("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 NexusEventDelegateTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__NexusEventDelegateTests = [
("testEventComponentAdded", testEventComponentAdded),
("testEventComponentRemoved", testEventComponentRemoved),
("testEventEntityCreated", testEventEntityCreated),
("testEventEntityDestroyed", testEventEntityDestroyed),
("testFamilyMemberRemoved", testFamilyMemberRemoved),
("testFamilyMemeberAdded", testFamilyMemeberAdded),
]
}
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(NexusEventDelegateTests.__allTests__NexusEventDelegateTests),
testCase(NexusTests.__allTests__NexusTests),
testCase(SingleTests.__allTests__SingleTests),
testCase(SparseSetTests.__allTests__SparseSetTests),
testCase(StateComponentMappingTests.__allTests__StateComponentMappingTests),
testCase(SystemsTests.__allTests__SystemsTests),
]
}
#endif

10
Tests/LinuxMain.swift Normal file
View File

@ -0,0 +1,10 @@
import XCTest
import FirebladeECSPerformanceTests
import FirebladeECSTests
var tests = [XCTestCaseEntry]()
tests += FirebladeECSPerformanceTests.__allTests()
tests += FirebladeECSTests.__allTests()
XCTMain(tests)

View File

@ -1,94 +0,0 @@
{
"$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"
]
}