diff --git a/.codecov.yml b/.codecov.yml index 4d717c6..1db9c06 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,6 @@ ignore: - "Tests/" + - "Benchmarks/" comment: - layout: header, changes, diff \ No newline at end of file + layout: header, changes, diff diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f3fc2f4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [ctreffs] +custom: ['https://www.paypal.com/donate?hosted_button_id=GCG3K54SKRALQ'] diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md new file mode 100644 index 0000000..3d6cc43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -0,0 +1,61 @@ +--- +name: 🐛 Bug Report +about: Something isn't working as expected, create a report to help us improve +labels: bug +--- + + + +### 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.* diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 0000000..74cd661 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,31 @@ +--- +name: 💡 Feature Request +about: A suggestion for a new feature or idea for this project +labels: enhancement +--- + + + +### 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.* diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..67bcb21 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord + url: https://discord.gg/JMM7W6pCCc + about: Questions or comments about using Fireblade? Ask here! diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..8838cc3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,52 @@ + + +### 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). diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml new file mode 100644 index 0000000..38fad3a --- /dev/null +++ b/.github/workflows/ci-linux.yml @@ -0,0 +1,39 @@ +name: Linux + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + linux-test-build-release: + 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.4.3 + 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 + diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml new file mode 100644 index 0000000..58641bb --- /dev/null +++ b/.github/workflows/ci-macos.yml @@ -0,0 +1,60 @@ +name: macOS + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + +env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + CODECOV_XCODE_VERSION: "15.4" # Xcode version used to generate code coverage + +jobs: + macos-test-build-release-xcode: + runs-on: macOS-latest + strategy: + matrix: + xcode: ["14.3.1", "15.4", "16.0"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Select Xcode ${{ matrix.xcode }} + run: sudo xcode-select -s /Applications/Xcode_${{ matrix.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.xcode }}.app/Contents/Developer + + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4.4.3 + with: + name: test-artifacts-${{ matrix.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.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.xcode == env.CODECOV_XCODE_VERSION + uses: codecov/codecov-action@v4.6.0 + with: + token: ${{ env.CODECOV_TOKEN }} + file: coverage.lcov + fail_ci_if_error: true diff --git a/.github/workflows/ci-wasm.yml b/.github/workflows/ci-wasm.yml new file mode 100644 index 0000000..0af195b --- /dev/null +++ b/.github/workflows/ci-wasm.yml @@ -0,0 +1,17 @@ +name: WASM + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + wasm-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + uses: swiftwasm/swiftwasm-action@v5.9 + with: + shell-action: swift build --triple wasm32-unknown-wasi diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml new file mode 100644 index 0000000..ab95135 --- /dev/null +++ b/.github/workflows/ci-windows.yml @@ -0,0 +1,39 @@ +name: Windows + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + windows-test-build-release: + runs-on: windows-latest + steps: + - name: Setup + uses: compnerd/gha-setup-swift@v0.2.3 + 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.4.3 + 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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 400a51e..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - macOS: - runs-on: macOS-latest - strategy: - matrix: - xcode: ["11.7", "12"] - steps: - - name: Checkout - uses: actions/checkout@master - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - name: Swift version - run: swift --version - - name: Test - run: swift test -v --skip-update --parallel --enable-test-discovery --enable-code-coverage - env: - DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer - - name: Generate coverage report - run: xcrun llvm-cov export -format="lcov" .build/debug/*PackageTests.xctest/Contents/MacOS/*PackageTests -instr-profile .build/debug/codecov/default.profdata > coverage.lcov - - name: Upload code coverage report - uses: codecov/codecov-action@master - with: - token: ${{secrets.CODECOV_TOKEN}} - file: coverage.lcov - fail_ci_if_error: true - - name: Build Release - run: swift build -c release - env: - DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer - - linux: - runs-on: ubuntu-latest - strategy: - matrix: - swift: ["5.1", "latest"] - container: - image: swift:${{ matrix.swift }} - steps: - - name: Checkout - uses: actions/checkout@master - - name: "Update APT" - shell: bash - run: "apt update" - - name: "Install curl" - shell: bash - run: "apt-get install -y curl" - - name: Swift version - run: swift --version - - name: Test - run: swift test -v --skip-update --parallel --enable-test-discovery --enable-code-coverage - - name: Generate coverage report - run: llvm-cov export -format="lcov" .build/debug/*PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > coverage.lcov - - name: Upload code coverage report - uses: codecov/codecov-action@master - with: - token: ${{secrets.CODECOV_TOKEN}} - file: coverage.lcov - fail_ci_if_error: true - - name: Build Release - run: swift build -c release - - - webAssembly: - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@master - - name: Swift version - run: swift --version - - name: Build - uses: swiftwasm/swiftwasm-action@main - with: - shell-action: swift build --triple wasm32-unknown-wasi diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index ed63bdb..d6768ce 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -18,8 +18,8 @@ jobs: steps: - uses: actions/checkout@master - - name: Generate documentation - uses: SwiftDocOrg/swift-doc@master + - name: Generate Swift Doc Documentation + uses: SwiftDocOrg/swift-doc@1.0.0-rc.1 with: inputs: "Sources/FirebladeECS" output: "Documentation" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 3db5809..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,25 +0,0 @@ -image: swift:5.1.3 - -stages: - - test - - build_release - -build_project: - stage: build_release - script: - - swift package reset - - swift build -c release - tags: - - docker - only: - - master - -test_project: - stage: test - variables: - GIT_DEPTH: "50" - script: - - swift package reset - - swift test - tags: - - docker \ No newline at end of file diff --git a/.swift-version b/.swift-version index cdb98d2..3659ea2 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.1.3 +5.8 diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..884cbf3 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,13 @@ +# file options +--exclude Sources/**/*.generated.swift +--exclude Sources/FirebladeECS/Entity+Component.swift # problems with self.get { } +--exclude Tests/**/*.swift + +# format options +--extensionacl on-declarations +--stripunusedargs closure-only +--commas inline +--self remove +--selfrequired get +--disable preferKeyPath +--disable opaqueGenericParameters \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index cfc6611..e32b7d4 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -9,23 +9,11 @@ identifier_name: line_length: 220 number_separator: minimum_length: 5 +analyzer_rules: + - explicit_self + - unused_declaration + - unused_import opt_in_rules: - #- explicit_acl - #- explicit_enum_raw_value - #- explicit_type_interface - #- extension_access_modifier - #- file_name - #- file_types_order - #- indentation_width - #- missing_docs - #- multiline_arguments_brackets - #- multiline_literal_brackets - #- multiline_parameters_brackets - #- no_grouping_extension - #- required_deinit - #- type_contents_order - #- unowned_variable_capture - - anyobject_protocol - array_init - attributes - closure_body_length @@ -48,7 +36,6 @@ opt_in_rules: - enum_case_associated_values_count - expiring_todo - explicit_init - - explicit_self - explicit_top_level_acl - fallthrough - fatal_error_message @@ -109,8 +96,6 @@ opt_in_rules: - unavailable_function - unneeded_parentheses_in_closure_argument - untyped_error_in_catch - - unused_declaration - - unused_import - vertical_parameter_alignment_on_call - vertical_whitespace_between_cases - vertical_whitespace_closing_braces diff --git a/Benchmarks/Benchmarks/ECSBenchmark/Base.swift b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift new file mode 100644 index 0000000..fe0da9d --- /dev/null +++ b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift @@ -0,0 +1,85 @@ +// +// Base.swift +// FirebladeECSTests +// +// Created by Christian Treffs on 09.10.17. +// + +import FirebladeECS + +class EmptyComponent: Component {} + +class Name: Component { + var name: String + init(name: String) { + self.name = name + } +} + +class Position: Component { + var x: Int + var y: Int + init(x: Int, y: Int) { + self.x = x + self.y = y + } +} + +class Velocity: Component { + var a: Float + init(a: Float) { + self.a = a + } +} + +class Party: Component { + var partying: Bool + init(partying: Bool) { + self.partying = partying + } +} + +class Color: Component { + var r: UInt8 = 0 + var g: UInt8 = 0 + var b: UInt8 = 0 +} + +class ExampleSystem { + private let family: Family2 + + 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 +} diff --git a/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift b/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift new file mode 100644 index 0000000..bceee0d --- /dev/null +++ b/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift @@ -0,0 +1,198 @@ +// swiftformat:disable preferForLoop +import Benchmark +import FirebladeECS + +// derived from FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift in the parent project + +let benchmarks = { + Benchmark("TraitMatching") { benchmark in + let nexus = setUpNexus() + let a = nexus.createEntity() + a.assign(Position(x: 1, y: 2)) + a.assign(Name(name: "myName")) + a.assign(Velocity(a: 3.14)) + a.assign(EmptyComponent()) + + let isMatch = nexus.family(requiresAll: Position.self, Velocity.self, + excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole( + isMatch.canBecomeMember(a) + ) + } + } + + Benchmark("TypedFamilyEntities") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entities + .forEach { (entity: Entity) in + _ = entity + } + ) + } + } + + Benchmark("TypedFamilyOneComponent") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position) in + _ = position + } + ) + } + } + + Benchmark("TypedFamilyEntityOneComponent") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position) in + _ = entity + _ = position + } + ) + } + } + + Benchmark("TypedFamilyTwoComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity) in + _ = position + _ = velocity + } + ) + } + } + Benchmark("TypedFamilyEntityTwoComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity) in + _ = entity + _ = position + _ = velocity + } + ) + } + } + + Benchmark("TypedFamilyThreeComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name) in + _ = position + _ = velocity + _ = name + } + ) + } + } + Benchmark("TypedFamilyEntityThreeComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name) in + _ = entity + _ = position + _ = velocity + _ = name + } + ) + } + } + + Benchmark("TypedFamilyFourComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name, color: Color) in + _ = position + _ = velocity + _ = name + _ = color + } + ) + } + } + + Benchmark("TypedFamilyEntityFourComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color) in + _ = entity + _ = position + _ = velocity + _ = name + _ = color + } + ) + } + } + + Benchmark("TypedFamilyFiveComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in + _ = position + _ = velocity + _ = name + _ = color + _ = empty + } + ) + } + } + + Benchmark("TypedFamilyEntityFiveComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole(family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in + _ = entity + _ = position + _ = velocity + _ = name + _ = color + _ = empty + } + ) + } + } +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 0000000..8be6cba --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.8 + +import PackageDescription + +let package = Package( + name: "ECSBenchmarks", + platforms: [ + .iOS(.v16), + .macOS(.v13) + ], + dependencies: [ + .package(path: "../"), + .package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.27.3")) + ], + 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") + ] + ) + ] +) diff --git a/Benchmarks/README.md b/Benchmarks/README.md new file mode 100644 index 0000000..0d525f6 --- /dev/null +++ b/Benchmarks/README.md @@ -0,0 +1,22 @@ +# Benchmarks for FirebladeECS + +Originally seeded by replicating performance tests into a new form leveraging [package-benchmark](https://swiftpackageindex.com/ordo-one/package-benchmark/) [Documentation](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark). + +To run all the available benchmarks: + + swift package benchmark --format markdown + +For more help on the package-benchmark SwiftPM plugin: + + swift package benchmark help + +Creating a local baseline: + + swift package --allow-writing-to-package-directory benchmark baseline update dev + swift package benchmark baseline list + +Comparing to a the baseline 'alpha' + + swift package benchmark baseline compare dev + +For more details on creating and comparing baselines, read [Creating and Comparing Benchmark Baselines](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark/creatingandcomparingbaselines). diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..5ac9aba --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,12 @@ +# This file is a list of the people responsible for ensuring that contributions +# to this projected are reviewed, either by themselves or by someone else. +# They are also the gatekeepers for their part of this project, with the final +# word on what goes in or not. +# The code owners file uses a .gitignore-like syntax to specify which parts of +# the codebase is associated with an owner. See +# +# 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 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..52ce96e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,136 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement +e.g. via [content abuse report][ref-report-abuse]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][ref-homepage-cc], +version 2.0, available at +. + +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 +. +Translations are available at +. + + + +[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/ + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8acc74..f3be4b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,18 +8,18 @@ The following is a set of **guidelines for contributing** to this project. Use your best judgment and feel free to propose changes to this document in a pull request. -**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) +**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) ### 💡 Your contribution - the sky is the limit 🌈 This is an open source project and we love to receive contributions from our community — [**you**][ref-contributors]! -There are many ways to contribute, from writing __tutorials__ or __blog posts__, improving the [__documentation__][ref-documentation], submitting __bug reports__ and __feature requests__ or -__writing code__ which can be incorporated into the repository itself. +There are many ways to contribute, from writing __tutorials__ or __blog posts__, improving the [__documentation__][ref-documentation], submitting [__bug reports__][ref-issues-new] and [__enhancement__][ref-pull-request-new] or +[__writing code__][ref-pull-request-new] which can be incorporated into the repository itself. -When contributing to this project, please feel free to discuss the change you wish to make via issue with the repository owners before making a change. +When contributing to this project, please feel free to discuss the changes and ideas you wish to contribute with the repository owners before making a change by opening a [new issue][ref-issues-new] and add the **feature request** tag to that issue. - +Note that we have a [code of conduct][ref-code-of-conduct], please follow it in all your interactions with the project. ### 🐞 You want to report a bug or file an issue? @@ -55,11 +55,12 @@ If you want to start somewhere, this would be a good place to start. That said, these aren't necessarily the easiest tickets. For any new contributions please consider these guidelines: + 1. Open a [new pull request (PR)][ref-pull-request-new] with a **clear and descriptive title** 2. Write a **detailed comment** with as much relevant information as possible including: - What your feature is intended to do? - How it can be used? - - What alternatives where considered? + - What alternatives where considered, if any? - Has this feature impact on performance or stability of the project? #### Your contribution responsibilities @@ -68,7 +69,7 @@ Don't be intimidated by these responsibilities, they are easy to meet if you tak - [x] Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback. - [x] Ensure (cross-)platform compatibility for every change that's accepted. An addition should not reduce the number of platforms that the project supports. -- [x] Ensure **coding conventions** are met. Lint your code with the project's default tools. +- [x] Ensure **coding conventions** are met. Lint your code with the project's default tools. Project wide commands are available through the [Makefile][ref-makefile] in the repository root. - [x] Add tests for your feature that prove it's working as expected. Code coverage should not drop below its previous value. - [x] Ensure none of the existing tests are failing after adding your changes. - [x] Document your public API code and ensure to add code comments where necessary. @@ -80,7 +81,7 @@ Please consult the [README][ref-readme] for installation instructions. -[ref-code-of-conduct]: t.b.d. +[ref-code-of-conduct]: https://github.com/fireblade-engine/ecs/blob/master/CODE_OF_CONDUCT.md [ref-contributors]: https://github.com/fireblade-engine/ecs/graphs/contributors [ref-documentation]: https://github.com/fireblade-engine/ecs/wiki [ref-gh-actions]: https://github.com/fireblade-engine/ecs/actions @@ -91,3 +92,4 @@ Please consult the [README][ref-readme] for installation instructions. [ref-pull-request-how-to]: https://docs.github.com/en/github/writing-on-github/autolinked-references-and-urls [ref-pull-request-new]: https://github.com/fireblade-engine/ecs/compare [ref-readme]: https://github.com/fireblade-engine/ecs/blob/master/README.md +[ref-makefile]: https://github.com/fireblade-engine/ecs/blob/master/Makefile diff --git a/Makefile b/Makefile index ecb7109..7518a32 100644 --- a/Makefile +++ b/Makefile @@ -1,79 +1,61 @@ -# Version 1.0.0 -UNAME_S := $(shell uname -s) +SWIFT_PACKAGE_VERSION := $(shell swift package tools-version) -# Lint -lint: - swiftlint autocorrect --format - swiftlint lint --quiet +# Lint fix and format code. +.PHONY: lint-fix +swiftlint: + mint run swiftlint lint --fix --config .swiftlint.yml --format --quiet +swiftformat: + mint run swiftformat . --swiftversion ${SWIFT_PACKAGE_VERSION} +lint-fix: swiftlint swiftformat -lintErrorOnly: - @swiftlint autocorrect --format --quiet - @swiftlint lint --quiet | grep error +# Generate code +.PHONY: generate-code +generate-code: + mint run sourcery --quiet --config ./.sourcery.yml + mint run sourcery --quiet --config ./.sourceryTests.yml -# Git -precommit: generateCode generateTestsCode lint genLinuxTests +# Run pre-push tasks +.PHONY: pre-push +pre-push: generate-code lint-fix -submodule: - git submodule init - git submodule update --recursive +.PHONY: precommit +precommit: pre-push -# Tests -genLinuxTests: - swift test --generate-linuxmain - swiftlint autocorrect --format --path Tests/ +.PHONY: setup-brew +setup-brew: + @which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + @brew update -test: genLinuxTests - swift test +.PHONY: install-dependencies-macOS +install-dependencies-macOS: setup-brew + brew install mint + mint bootstrap -# Package -latest: - swift package update +.PHONY: setupEnvironment +setupEnvironment: install-dependencies-macOS -resolve: - swift package resolve +# Build debug version +.PHONY: build-debug +build-debug: + swift build -c debug -# Xcode -genXcode: - swift package generate-xcodeproj --enable-code-coverage --skip-extra-files - -genXcodeOpen: genXcode - open *.xcodeproj - -# Clean -clean: - swift package reset - -rm -rdf .swiftpm/xcode - -rm -rdf .build/ - -rm Package.resolved - -rm .DS_Store - -cleanArtifacts: - swift package clean +# Build release version +.PHONY: build-release +build-release: + swift build -c release --skip-update # Test links in README # requires +.PHONY: testReadme testReadme: markdown-link-check -p -v ./README.md -generateCode: - sourcery --config ./.sourcery.yml --verbose -generateTestsCode: - sourcery --config ./.sourceryTests.yml --verbose +# Delete package build artifacts. +.PHONY: clean +clean: clean-sourcery + swift package clean -brewInstallDeps: brewUpdate - brew install swiftenv - brew install swiftlint - brew install sourcery - -brewSetup: - which -s brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" - -brewUpdate: brewSetup - brew update - -setupEnvironment: brewInstallDeps - open Package.swift - -# lines of code -loc: clean - find . -name "*.swift" -print0 | xargs -0 wc -l +# Clean sourcery cache +.PHONY: clean-sourcery +clean-sourcery: + rm -rdf ${HOME}/Library/Caches/Sourcery diff --git a/Mintfile b/Mintfile new file mode 100644 index 0000000..cf6a988 --- /dev/null +++ b/Mintfile @@ -0,0 +1,3 @@ +realm/SwiftLint@0.57.0 +nicklockwood/SwiftFormat@0.54.6 +krzysztofzablocki/Sourcery@2.2.5 diff --git a/Package.swift b/Package.swift index 36f03a2..424738d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.8 import PackageDescription let package = Package( @@ -8,9 +8,11 @@ let package = Package( targets: ["FirebladeECS"]) ], targets: [ - .target(name: "FirebladeECS"), + .target(name: "FirebladeECS", + exclude: ["Stencils/Family.stencil"]), .testTarget(name: "FirebladeECSTests", - dependencies: ["FirebladeECS"]), + dependencies: ["FirebladeECS"], + exclude: ["Stencils/FamilyTests.stencil"]), .testTarget(name: "FirebladeECSPerformanceTests", dependencies: ["FirebladeECS"]) ], diff --git a/README.md b/README.md index 778294d..d554942 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # Fireblade ECS (Entity-Component System) [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) -[![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) +[![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) +[![codecov](https://codecov.io/gh/fireblade-engine/ecs/branch/master/graph/badge.svg)](https://codecov.io/gh/fireblade-engine/ecs) [![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). @@ -23,19 +25,19 @@ These instructions will get you a copy of the project up and running on your loc ### 💻 Installing -Fireblade ECS is available for all platforms that support [Swift 5.1](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager). +Fireblade ECS is available for all platforms that support [Swift 5.8](https://swift.org/) and higher and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager). Extend the following lines in your `Package.swift` file or use it to create a new project. ```swift -// swift-tools-version:5.1 +// swift-tools-version:5.8 import PackageDescription let package = Package( name: "YourPackageName", dependencies: [ - .package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.3") + .package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.17.5") ], targets: [ .target( diff --git a/Sources/FirebladeECS/CodingStrategy.swift b/Sources/FirebladeECS/CodingStrategy.swift index 7f06ac6..4e46476 100644 --- a/Sources/FirebladeECS/CodingStrategy.swift +++ b/Sources/FirebladeECS/CodingStrategy.swift @@ -13,6 +13,6 @@ public struct DynamicCodingKey: CodingKey { public var intValue: Int? public var stringValue: String - public init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" } + public init?(intValue: Int) { self.intValue = intValue; stringValue = "\(intValue)" } public init?(stringValue: String) { self.stringValue = stringValue } } diff --git a/Sources/FirebladeECS/ComponentIdentifier.swift b/Sources/FirebladeECS/ComponentIdentifier.swift index 10ac0dc..9bf8905 100644 --- a/Sources/FirebladeECS/ComponentIdentifier.swift +++ b/Sources/FirebladeECS/ComponentIdentifier.swift @@ -13,12 +13,12 @@ public struct ComponentIdentifier { extension ComponentIdentifier { @usableFromInline - init(_ componentType: C.Type) where C: Component { - self.id = Self.makeRuntimeHash(componentType) + init(_ componentType: (some Component).Type) { + id = Self.makeRuntimeHash(componentType) } /// object identifier hash (only stable during runtime) - arbitrary hash is ok. - internal static func makeRuntimeHash(_ componentType: C.Type) -> Identifier where C: Component { + static func makeRuntimeHash(_ componentType: (some Component).Type) -> Identifier { ObjectIdentifier(componentType).hashValue } @@ -34,5 +34,5 @@ extension ComponentIdentifier { } } -extension ComponentIdentifier: Equatable { } -extension ComponentIdentifier: Hashable { } +extension ComponentIdentifier: Equatable {} +extension ComponentIdentifier: Hashable {} diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 33e3ff2..6ab62cf 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -17,9 +17,9 @@ public struct Entity { /// The unique entity identifier. public private(set) var identifier: EntityIdentifier - internal init(nexus: Nexus, id: EntityIdentifier) { + init(nexus: Nexus, id: EntityIdentifier) { self.nexus = nexus - self.identifier = id + identifier = id } /// Returns the number of components for this entity. @@ -38,13 +38,13 @@ public struct Entity { } @discardableResult - public func createEntity(with components: C) -> Entity where C: Collection, C.Element == Component { + public func createEntity(with components: some Collection) -> Entity { nexus.createEntity(with: components) } /// Checks if a component with given type is assigned to this entity. /// - Parameter type: the component type. - public func has(_ type: C.Type) -> Bool where C: Component { + public func has(_ type: (some Component).Type) -> Bool { 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: C) -> Entity where C: Component { + public func assign(_ component: some Component) -> Entity { assign(component) return self } @discardableResult - public func assign(_ components: C) -> Entity where C: Collection, C.Element == Component { + public func assign(_ components: some Collection) -> Entity { 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: C) -> Entity where C: Component { + public func remove(_ component: some Component) -> Entity { remove(component.identifier) } /// Remove a component by type from this entity. /// - Parameter compType: the component type. @discardableResult - public func remove(_ compType: C.Type) -> Entity where C: Component { + public func remove(_ compType: (some Component).Type) -> Entity { 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,8 +144,9 @@ 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 { diff --git a/Sources/FirebladeECS/EntityIdentifier.swift b/Sources/FirebladeECS/EntityIdentifier.swift index 6c2e98e..f8676b6 100644 --- a/Sources/FirebladeECS/EntityIdentifier.swift +++ b/Sources/FirebladeECS/EntityIdentifier.swift @@ -23,8 +23,8 @@ public struct EntityIdentifier { } } -extension EntityIdentifier: Equatable { } -extension EntityIdentifier: Hashable { } +extension EntityIdentifier: Equatable {} +extension EntityIdentifier: Hashable {} extension EntityIdentifier: RawRepresentable { /// The entity identifier represented as a raw value. @@ -33,7 +33,7 @@ extension EntityIdentifier: RawRepresentable { @inlinable public init(rawValue: Identifier) { - self.id = rawValue + id = rawValue } } diff --git a/Sources/FirebladeECS/EntityIdentifierGenerator.swift b/Sources/FirebladeECS/EntityIdentifierGenerator.swift index 345493d..9d5f6bc 100644 --- a/Sources/FirebladeECS/EntityIdentifierGenerator.swift +++ b/Sources/FirebladeECS/EntityIdentifierGenerator.swift @@ -49,10 +49,10 @@ public struct LinearIncrementingEntityIdGenerator: EntityIdentifierGenerator { @usableFromInline init(startProviding initialEntityIds: EntityIds) where EntityIds: BidirectionalCollection, EntityIds.Element == EntityIdentifier { - let initialInUse: [EntityIdentifier.Identifier] = initialEntityIds.map { $0.id } + let initialInUse: [EntityIdentifier.Identifier] = initialEntityIds.map(\.id) let maxInUseValue = initialInUse.max() ?? 0 let inUseSet = Set(initialInUse) // a set of all eIds in use - let allSet = Set(0...maxInUseValue) // all eIds from 0 to including maxInUseValue + let allSet = Set(0 ... maxInUseValue) // all eIds from 0 to including maxInUseValue let freeSet = allSet.subtracting(inUseSet) // all "holes" / unused / free eIds let initialFree = Array(freeSet).sorted().reversed() // order them to provide them linear increasing after all initially used are provided. stack = initialFree + initialInUse @@ -83,12 +83,12 @@ public struct LinearIncrementingEntityIdGenerator: EntityIdentifierGenerator { @inlinable public init(startProviding initialEntityIds: EntityIds) where EntityIds: BidirectionalCollection, EntityIds.Element == EntityIdentifier { - self.storage = Storage(startProviding: initialEntityIds) + storage = Storage(startProviding: initialEntityIds) } @inlinable public init() { - self.storage = Storage() + storage = Storage() } @inline(__always) diff --git a/Sources/FirebladeECS/FSM.swift b/Sources/FirebladeECS/FSM.swift index 9a8de0d..4324ada 100644 --- a/Sources/FirebladeECS/FSM.swift +++ b/Sources/FirebladeECS/FSM.swift @@ -97,9 +97,7 @@ extension ComponentTypeProvider: ComponentProvider { /// This component provider always returns the same instance of the component. The instance /// is created when first required and is of the type passed in to the initializer. public final class ComponentSingletonProvider { - private lazy var instance: Component = { - componentType.init() - }() + private lazy var instance: Component = componentType.init() private var componentType: ComponentInitializable.Type @@ -171,7 +169,7 @@ extension DynamicComponentProvider: ComponentProvider { /// Represents a state for an EntityStateMachine. The state contains any number of ComponentProviders which /// are used to add components to the entity when this state is entered. public class EntityState { - internal var providers = [ComponentIdentifier: ComponentProvider]() + var providers = [ComponentIdentifier: ComponentProvider]() public init() {} @@ -253,7 +251,7 @@ extension EntityState { /// - Returns: This EntityState, so more modifications can be applied. @inline(__always) @discardableResult - public func addProvider(type: C.Type, provider: ComponentProvider) -> Self { + public func addProvider(type: (some ComponentInitializable).Type, provider: ComponentProvider) -> Self { addMapping(for: type).withProvider(provider) return self } @@ -272,7 +270,7 @@ public class StateComponentMapping { /// by more specific mappings if other methods are called. /// - Parameter creatingState: The EntityState that the mapping will belong to /// - Parameter type: The component type for the mapping - internal init(creatingState: EntityState, type: ComponentInitializable.Type) { + init(creatingState: EntityState, type: ComponentInitializable.Type) { self.creatingState = creatingState componentType = type provider = ComponentTypeProvider(type: type) @@ -318,7 +316,7 @@ public class StateComponentMapping { /// - Parameter closure: The Closure instance to return the component instance /// - Returns: This ComponentMapping, so more modifications can be applied @discardableResult - public func withMethod(_ closure: DynamicComponentProvider.Closure) -> Self { + public func withMethod(_ closure: DynamicComponentProvider.Closure) -> Self { setProvider(DynamicComponentProvider(closure: closure)) return self } @@ -403,7 +401,7 @@ public class EntityStateMachine { var toAdd: [ComponentIdentifier: ComponentProvider] - if let currentState = currentState { + if let currentState { toAdd = .init() for (identifier, provider) in newState.providers { toAdd[identifier] = provider @@ -411,7 +409,8 @@ public class EntityStateMachine { 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) diff --git a/Sources/FirebladeECS/Family+Coding.swift b/Sources/FirebladeECS/Family+Coding.swift index 8de8699..1048d07 100644 --- a/Sources/FirebladeECS/Family+Coding.swift +++ b/Sources/FirebladeECS/Family+Coding.swift @@ -14,6 +14,7 @@ extension CodingUserInfoKey { } // MARK: - encoding + extension FamilyMemberContainer: Encodable where R: FamilyEncoding { func encode(to encoder: Encoder) throws { let strategy = encoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() @@ -50,11 +51,12 @@ extension Family where R: FamilyEncoding { } // MARK: - decoding + extension FamilyMemberContainer: Decodable where R: FamilyDecoding { init(from decoder: Decoder) throws { var familyContainer = try decoder.unkeyedContainer() let strategy = decoder.userInfo[.nexusCodingStrategy] as? CodingStrategy ?? DefaultCodingStrategy() - self.components = try R.decode(componentsIn: &familyContainer, using: strategy) + components = try R.decode(componentsIn: &familyContainer, using: strategy) } } diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index ba5749e..9b2f13d 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -53,7 +53,7 @@ public struct Family where R: FamilyRequirementsManaging { /// - Returns: The newly created member entity. @discardableResult public func createMember(@FamilyMemberBuilder using builder: () -> R.Components) -> Entity { - self.createMember(with: builder()) + createMember(with: builder()) } } @@ -70,16 +70,17 @@ extension Family: Sequence { } } -extension Family: LazySequenceProtocol { } +extension Family: LazySequenceProtocol {} // MARK: - components iterator + extension Family { public struct ComponentsIterator: IteratorProtocol { @usableFromInline var memberIdsIterator: UnorderedSparseSet.ElementIterator @usableFromInline unowned let nexus: Nexus public init(family: Family) { - self.nexus = family.nexus + nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -93,10 +94,11 @@ extension Family { } } -extension Family.ComponentsIterator: LazySequenceProtocol { } -extension Family.ComponentsIterator: Sequence { } +extension Family.ComponentsIterator: LazySequenceProtocol {} +extension Family.ComponentsIterator: Sequence {} // MARK: - entity iterator + extension Family { @inlinable public var entities: EntityIterator { EntityIterator(family: self) @@ -107,7 +109,7 @@ extension Family { @usableFromInline unowned let nexus: Nexus public init(family: Family) { - self.nexus = family.nexus + nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -120,10 +122,11 @@ extension Family { } } -extension Family.EntityIterator: LazySequenceProtocol { } -extension Family.EntityIterator: Sequence { } +extension Family.EntityIterator: LazySequenceProtocol {} +extension Family.EntityIterator: Sequence {} // MARK: - entity component iterator + extension Family { @inlinable public var entityAndComponents: EntityComponentIterator { EntityComponentIterator(family: self) @@ -134,7 +137,7 @@ extension Family { @usableFromInline unowned let nexus: Nexus public init(family: Family) { - self.nexus = family.nexus + nexus = family.nexus memberIdsIterator = family.memberIds.makeIterator() } @@ -147,10 +150,11 @@ extension Family { } } -extension Family.EntityComponentIterator: LazySequenceProtocol { } -extension Family.EntityComponentIterator: Sequence { } +extension Family.EntityComponentIterator: LazySequenceProtocol {} +extension Family.EntityComponentIterator: Sequence {} // MARK: - member creation + extension Family { /// Create a new entity with components required by this family. /// diff --git a/Sources/FirebladeECS/FamilyMemberBuilder.swift b/Sources/FirebladeECS/FamilyMemberBuilder.swift index c1c79bf..1d281fd 100644 --- a/Sources/FirebladeECS/FamilyMemberBuilder.swift +++ b/Sources/FirebladeECS/FamilyMemberBuilder.swift @@ -5,6 +5,10 @@ // Created by Christian Treffs on 07.08.20. // -@_functionBuilder -public enum FamilyMemberBuilderPreview where R: FamilyRequirementsManaging { } -public typealias FamilyMemberBuilder = FamilyMemberBuilderPreview where R: FamilyRequirementsManaging +#if swift(<5.4) + @_functionBuilder + public enum FamilyMemberBuilder where R: FamilyRequirementsManaging {} +#else + @resultBuilder + public enum FamilyMemberBuilder where R: FamilyRequirementsManaging {} +#endif diff --git a/Sources/FirebladeECS/FamilyTraitSet.swift b/Sources/FirebladeECS/FamilyTraitSet.swift index 3da947e..8da4a2e 100644 --- a/Sources/FirebladeECS/FamilyTraitSet.swift +++ b/Sources/FirebladeECS/FamilyTraitSet.swift @@ -19,7 +19,7 @@ public struct FamilyTraitSet { self.requiresAll = requiresAll self.excludesAll = excludesAll - self.setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll]) + setHash = FirebladeECS.hash(combine: [requiresAll, excludesAll]) } @inlinable diff --git a/Sources/FirebladeECS/Foundation+Extensions.swift b/Sources/FirebladeECS/Foundation+Extensions.swift index c7ff0cf..4ae06f1 100644 --- a/Sources/FirebladeECS/Foundation+Extensions.swift +++ b/Sources/FirebladeECS/Foundation+Extensions.swift @@ -6,8 +6,8 @@ // #if canImport(Foundation) -import Foundation + import Foundation -extension JSONEncoder: TopLevelEncoder { } -extension JSONDecoder: TopLevelDecoder { } + extension JSONEncoder: TopLevelEncoder {} + extension JSONDecoder: TopLevelDecoder {} #endif diff --git a/Sources/FirebladeECS/Generated/Family.generated.swift b/Sources/FirebladeECS/Generated/Family.generated.swift index caa2ff2..00d67e4 100644 --- a/Sources/FirebladeECS/Generated/Family.generated.swift +++ b/Sources/FirebladeECS/Generated/Family.generated.swift @@ -1,6 +1,5 @@ -// Generated using Sourcery 1.0.0 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.5 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT - // swiftlint:disable file_length // swiftlint:disable function_parameter_count // swiftlint:disable large_tuple diff --git a/Sources/FirebladeECS/Hashing.swift b/Sources/FirebladeECS/Hashing.swift index d319029..033b946 100644 --- a/Sources/FirebladeECS/Hashing.swift +++ b/Sources/FirebladeECS/Hashing.swift @@ -6,11 +6,11 @@ // #if arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) // 64 bit -private let kFibA: UInt = 0x9e3779b97f4a7c15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio) + private let kFibA: UInt = 0x9E37_79B9_7F4A_7C15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio) #elseif arch(i386) || arch(arm) || os(watchOS) || arch(wasm32) // 32 bit -private let kFibA: UInt = 0x9e3779b9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio) + private let kFibA: UInt = 0x9E37_79B9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio) #else -#error("unsupported architecture") + #error("unsupported architecture") #endif /// entity id ^ component identifier hash @@ -20,6 +20,7 @@ public typealias EntityComponentHash = Int public typealias ComponentTypeHash = Int // MARK: - hash combine + /// Calculates the combined hash of two values. This implementation is based on boost::hash_combine. /// Will always produce the same result for the same combination of seed and value during the single run of a program. /// @@ -55,27 +56,29 @@ public func hash(combine hashValues: H) -> Int where H.Element: Has } // MARK: - entity component hash + extension EntityComponentHash { - internal static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash { + static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash { let entityIdSwapped = UInt(entityId.id).byteSwapped // needs to be 64 bit let componentTypeHashUInt = UInt(bitPattern: componentTypeHash) let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped return Int(bitPattern: hashUInt) } - internal static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash { + static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash { let entityIdSwapped = UInt(entityId.id).byteSwapped let entityIdSwappedInt = Int(bitPattern: entityIdSwapped) return hash ^ entityIdSwappedInt } - internal static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier { + static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier { let entityId: Int = (hash ^ componentTypeHash).byteSwapped return EntityIdentifier(UInt32(truncatingIfNeeded: entityId)) } } // MARK: - string hashing + /// public enum StringHashing { /// *Waren Singer djb2* @@ -85,7 +88,7 @@ public enum StringHashing { var hash: UInt64 = 5381 var iter = utf8String.unicodeScalars.makeIterator() while let char = iter.next() { - hash = 127 * (hash & 0xFFFFFFFFFFFFFF) &+ UInt64(char.value) + hash = 127 * (hash & 0xFF_FFFF_FFFF_FFFF) &+ UInt64(char.value) } return hash } diff --git a/Sources/FirebladeECS/ManagedContiguousArray.swift b/Sources/FirebladeECS/ManagedContiguousArray.swift index 5d627b1..9f00950 100644 --- a/Sources/FirebladeECS/ManagedContiguousArray.swift +++ b/Sources/FirebladeECS/ManagedContiguousArray.swift @@ -92,6 +92,7 @@ public struct ManagedContiguousArray { } // MARK: - Equatable + extension ManagedContiguousArray: Equatable where Element: Equatable { public static func == (lhs: ManagedContiguousArray, rhs: ManagedContiguousArray) -> Bool { lhs.store == rhs.store @@ -99,4 +100,5 @@ extension ManagedContiguousArray: Equatable where Element: Equatable { } // MARK: - Codable -extension ManagedContiguousArray: Codable where Element: Codable { } + +extension ManagedContiguousArray: Codable where Element: Codable {} diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 33f0819..271f6f0 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -28,12 +28,12 @@ extension Nexus { } @discardableResult - public final func assign(component: C, to entity: Entity) -> Bool where C: Component { + public final func assign(component: some Component, to entity: Entity) -> Bool { assign(component: component, to: entity) } @discardableResult - public final func assign(components: C, to entity: Entity) -> Bool where C: Collection, C.Element == Component { + public final func assign(components: some Collection, to entity: Entity) -> Bool { assign(components: components, to: entity.identifier) } @@ -93,7 +93,7 @@ extension Nexus { return false } var iter = allComponents.makeIterator() - var removedAll: Bool = true + var removedAll = true while let component = iter.next() { removedAll = removedAll && remove(component: component, from: entityId) } diff --git a/Sources/FirebladeECS/Nexus+ComponentsBuilder.swift b/Sources/FirebladeECS/Nexus+ComponentsBuilder.swift index e48bab5..d1cd65b 100644 --- a/Sources/FirebladeECS/Nexus+ComponentsBuilder.swift +++ b/Sources/FirebladeECS/Nexus+ComponentsBuilder.swift @@ -5,9 +5,13 @@ // Created by Christian Treffs on 30.07.20. // -@_functionBuilder -public enum ComponentsBuilderPreview { } -public typealias ComponentsBuilder = ComponentsBuilderPreview +#if swift(<5.4) + @_functionBuilder + public enum ComponentsBuilder {} +#else + @resultBuilder + public enum ComponentsBuilder {} +#endif extension ComponentsBuilder { public static func buildBlock(_ components: Component...) -> [Component] { @@ -35,7 +39,7 @@ extension Nexus { /// - Returns: The newly created entity with the provided component assigned. @discardableResult public func createEntity(@ComponentsBuilder using builder: () -> Component) -> Entity { - self.createEntity(with: builder()) + createEntity(with: builder()) } /// Create an entity assigning multiple components. @@ -51,7 +55,7 @@ extension Nexus { /// - Returns: The newly created entity with the provided components assigned. @discardableResult public func createEntity(@ComponentsBuilder using builder: () -> [Component]) -> Entity { - self.createEntity(with: builder()) + createEntity(with: builder()) } /// Create multiple entities assigning one component each. @@ -68,7 +72,7 @@ extension Nexus { /// - Returns: The newly created entities with the provided component assigned. @discardableResult public func createEntities(count: Int, @ComponentsBuilder using builder: (ComponentsBuilder.Context) -> Component) -> [Entity] { - (0.. [Component] = { _ in [] }) -> [Entity] { - (0..(with components: C) -> Entity where C: Collection, C.Element == Component { - let entity = self.createEntity() + public func createEntity(with components: some Collection) -> Entity { + let entity = createEntity() assign(components: components, to: entity.identifier) return entity } @@ -50,7 +50,7 @@ extension Nexus { @discardableResult public func destroy(entity: Entity) -> Bool { - self.destroy(entityId: entity.identifier) + destroy(entityId: entity.identifier) } @discardableResult @@ -76,6 +76,7 @@ extension Nexus { } // MARK: - entities iterator + extension Nexus { public struct EntitiesIterator: IteratorProtocol { private var iterator: AnyIterator @@ -96,5 +97,6 @@ extension Nexus { } } } -extension Nexus.EntitiesIterator: LazySequenceProtocol { } -extension Nexus.EntitiesIterator: Sequence { } + +extension Nexus.EntitiesIterator: LazySequenceProtocol {} +extension Nexus.EntitiesIterator: Sequence {} diff --git a/Sources/FirebladeECS/Nexus+Internal.swift b/Sources/FirebladeECS/Nexus+Internal.swift index 8feafa0..b387303 100644 --- a/Sources/FirebladeECS/Nexus+Internal.swift +++ b/Sources/FirebladeECS/Nexus+Internal.swift @@ -8,7 +8,7 @@ extension Nexus { @usableFromInline @discardableResult - func assign(components: C, to entityId: EntityIdentifier) -> Bool where C: Collection, C.Element == Component { + func assign(components: some Collection, to entityId: EntityIdentifier) -> Bool { var iter = components.makeIterator() while let component = iter.next() { let componentId = component.identifier @@ -103,8 +103,8 @@ extension Nexus { return } - let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits) - if !exists(entity: entityId) && isMember { + let isMember: Bool = isMember(entity: entityId, inFamilyWithTraits: traits) + if !exists(entity: entityId), isMember { remove(entityWithId: entityId, fromFamilyWithTraits: traits) return } diff --git a/Sources/FirebladeECS/Nexus.swift b/Sources/FirebladeECS/Nexus.swift index a69a856..7f0f413 100644 --- a/Sources/FirebladeECS/Nexus.swift +++ b/Sources/FirebladeECS/Nexus.swift @@ -47,13 +47,14 @@ public final class Nexus { codingStrategy: DefaultCodingStrategy()) } - internal init(componentsByType: [ComponentIdentifier: ManagedContiguousArray], - componentsByEntity: [EntityIdentifier: Set], - entityIdGenerator: EntityIdentifierGenerator, - familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet], - codingStrategy: CodingStrategy) { + init(componentsByType: [ComponentIdentifier: ManagedContiguousArray], + componentsByEntity: [EntityIdentifier: Set], + entityIdGenerator: EntityIdentifierGenerator, + familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet], + codingStrategy: CodingStrategy) + { self.componentsByType = componentsByType - self.componentIdsByEntity = componentsByEntity + componentIdsByEntity = componentsByEntity self.familyMembersByTraits = familyMembersByTraits self.entityIdGenerator = entityIdGenerator self.codingStrategy = codingStrategy @@ -71,6 +72,7 @@ public final class Nexus { } // MARK: - CustomDebugStringConvertible + extension Nexus: CustomDebugStringConvertible { public var debugDescription: String { "" @@ -78,8 +80,9 @@ extension Nexus: CustomDebugStringConvertible { } // MARK: - default coding strategy + public struct DefaultCodingStrategy: CodingStrategy { - public init() { } + public init() {} public func codingKey(for componentType: C.Type) -> DynamicCodingKey where C: Component { DynamicCodingKey(stringValue: "\(C.self)").unsafelyUnwrapped diff --git a/Sources/FirebladeECS/NexusEvent.swift b/Sources/FirebladeECS/NexusEvent.swift index 7bc7b38..555effc 100644 --- a/Sources/FirebladeECS/NexusEvent.swift +++ b/Sources/FirebladeECS/NexusEvent.swift @@ -1,5 +1,5 @@ // -// NexusEvents.swift +// NexusEvent.swift // FirebladeECS // // Created by Christian Treffs on 08.10.17. diff --git a/Sources/FirebladeECS/Single.swift b/Sources/FirebladeECS/Single.swift index 36d2c8e..2d64936 100644 --- a/Sources/FirebladeECS/Single.swift +++ b/Sources/FirebladeECS/Single.swift @@ -32,13 +32,13 @@ extension Single where A: SingleComponent { } public var entity: Entity { - Entity(nexus: self.nexus, id: entityId) + Entity(nexus: nexus, id: entityId) } } extension Nexus { public func single(_ component: S.Type) -> Single where S: SingleComponent { - let family = self.family(requires: S.self) + let family = family(requires: S.self) precondition(family.count <= 1, "Singleton count of \(S.self) must be 0 or 1: \(family.count)") let entityId: EntityIdentifier if family.isEmpty { diff --git a/Sources/FirebladeECS/UnorderedSparseSet.swift b/Sources/FirebladeECS/UnorderedSparseSet.swift index dc6fd79..7999b5a 100644 --- a/Sources/FirebladeECS/UnorderedSparseSet.swift +++ b/Sources/FirebladeECS/UnorderedSparseSet.swift @@ -74,7 +74,7 @@ public struct UnorderedSparseSet { guard let denseIndex = findIndex(at: key) else { return nil } - let entry = self.dense[denseIndex] + let entry = dense[denseIndex] assert(entry.key == key, "entry.key and findIndex(at: key) must be equal!") return entry.element } @@ -99,7 +99,7 @@ public struct UnorderedSparseSet { } 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 } @@ -208,12 +208,14 @@ extension UnorderedSparseSet where Key == Int { } // MARK: - Sequence + extension UnorderedSparseSet: Sequence { public func makeIterator() -> ElementIterator { ElementIterator(self) } // MARK: - UnorderedSparseSetIterator + public struct ElementIterator: IteratorProtocol { var iterator: IndexingIterator> @@ -226,17 +228,20 @@ extension UnorderedSparseSet: Sequence { } } } -extension UnorderedSparseSet.ElementIterator: LazySequenceProtocol { } -extension UnorderedSparseSet.ElementIterator: Sequence { } + +extension UnorderedSparseSet.ElementIterator: LazySequenceProtocol {} +extension UnorderedSparseSet.ElementIterator: Sequence {} // MARK: - Equatable -extension UnorderedSparseSet.Storage.Entry: Equatable where Element: Equatable { } + +extension UnorderedSparseSet.Storage.Entry: Equatable where Element: Equatable {} extension UnorderedSparseSet.Storage: Equatable where Element: Equatable { @usableFromInline static func == (lhs: UnorderedSparseSet.Storage, rhs: UnorderedSparseSet.Storage) -> Bool { lhs.dense == rhs.dense && lhs.sparse == rhs.sparse } } + extension UnorderedSparseSet: Equatable where Element: Equatable { public static func == (lhs: UnorderedSparseSet, rhs: UnorderedSparseSet) -> Bool { lhs.storage == rhs.storage @@ -244,6 +249,7 @@ extension UnorderedSparseSet: Equatable where Element: Equatable { } // MARK: - Codable -extension UnorderedSparseSet.Storage.Entry: Codable where Element: Codable { } -extension UnorderedSparseSet.Storage: Codable where Element: Codable { } -extension UnorderedSparseSet: Codable where Element: Codable { } + +extension UnorderedSparseSet.Storage.Entry: Codable where Element: Codable {} +extension UnorderedSparseSet.Storage: Codable where Element: Codable {} +extension UnorderedSparseSet: Codable where Element: Codable {} diff --git a/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift index 8446ebf..5b64f5e 100644 --- a/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/HashingPerformanceTests.swift @@ -5,6 +5,7 @@ // Created by Christian Treffs on 14.02.19. // +#if os(macOS) import FirebladeECS import XCTest @@ -97,3 +98,6 @@ class HashingPerformanceTests: XCTestCase { #endif } } +#else + #warning("Skipping HashingPerformanceTests") +#endif diff --git a/Tests/FirebladeECSPerformanceTests/TypeIdentifierPerformanceTests.swift b/Tests/FirebladeECSPerformanceTests/TypeIdentifierPerformanceTests.swift index 661ee98..e073174 100644 --- a/Tests/FirebladeECSPerformanceTests/TypeIdentifierPerformanceTests.swift +++ b/Tests/FirebladeECSPerformanceTests/TypeIdentifierPerformanceTests.swift @@ -5,6 +5,7 @@ // Created by Christian Treffs on 05.10.19. // +#if os(macOS) import FirebladeECS import XCTest @@ -30,7 +31,7 @@ final class TypeIdentifierPerformanceTests: XCTestCase { /// release: 1.034 sec /// debug: func testPerformanceHash() { - measure { + measure(options: .default) { for _ in 0.. [XCTestCaseEntry] { - return [ - testCase(ComponentIdentifierTests.__allTests__ComponentIdentifierTests), - testCase(HashingPerformanceTests.__allTests__HashingPerformanceTests), - testCase(TypeIdentifierPerformanceTests.__allTests__TypeIdentifierPerformanceTests), - testCase(TypedFamilyPerformanceTests.__allTests__TypedFamilyPerformanceTests), - ] -} -#endif diff --git a/Tests/FirebladeECSTests/Generated/FamilyTests.generated.swift b/Tests/FirebladeECSTests/Generated/FamilyTests.generated.swift index 3f0b988..345fcf9 100644 --- a/Tests/FirebladeECSTests/Generated/FamilyTests.generated.swift +++ b/Tests/FirebladeECSTests/Generated/FamilyTests.generated.swift @@ -1,6 +1,5 @@ -// Generated using Sourcery 1.0.0 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.5 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT - import FirebladeECS import XCTest diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 7889cf4..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,10 +0,0 @@ -import XCTest - -import FirebladeECSPerformanceTests -import FirebladeECSTests - -var tests = [XCTestCaseEntry]() -tests += FirebladeECSPerformanceTests.__allTests() -tests += FirebladeECSTests.__allTests() - -XCTMain(tests) diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..e6838fd --- /dev/null +++ b/renovate.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base"], + "packageRules": [ + { + "description": "Accumulate non-major updates into one pull request", + "matchUpdateTypes": ["minor", "patch"], + "matchCurrentVersion": ">=1", + "groupName": "all non-major dependencies", + "groupSlug": "all-minor-patch" + } + ] +}