Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4c8d36b
chore(conductor): Add new track 'Refactor the ECS to exclusively use …
ctreffs Feb 13, 2026
6291877
feat(phase): Complete API Design & Prototyping
ctreffs Feb 13, 2026
3d59f31
feat(phase): Complete Core Refactoring (Family & Nexus) including Cod…
ctreffs Feb 13, 2026
7836aec
feat(phase): Complete API Adoption & Cleanup
ctreffs Feb 13, 2026
7dd4317
chore(conductor): Mark track 'Refactor the ECS to exclusively use par…
ctreffs Feb 13, 2026
0bb44ab
feat(tests): Add comprehensive component tests (1-8) and fix encoding
ctreffs Feb 13, 2026
114f8ff
chore(conductor): Mark track 'Refactor the ECS to exclusively use par…
ctreffs Feb 13, 2026
923c85e
No conductor files
ctreffs Feb 13, 2026
c507b3d
Cleanups
ctreffs Feb 13, 2026
4783ea7
fix(review): Address PR feedback (restore Sequence, Coding, optimize …
ctreffs Feb 13, 2026
9fc888b
fix(docs): Update documentation and address remaining review comments
ctreffs Feb 13, 2026
76114b7
docs(conductor): Update plan with review tasks
ctreffs Feb 13, 2026
ce2b5d5
fix(coding): Restore Coding Feature Parity and Fix Tests
ctreffs Feb 13, 2026
b5126c6
docs(conductor): Update plan with coding parity task
ctreffs Feb 13, 2026
d4147c1
Restore family
ctreffs Feb 13, 2026
2b925a3
Restore Family+Coding [broken]
ctreffs Feb 13, 2026
6702b6a
Valid updates
ctreffs Feb 13, 2026
c863cd1
Coding
ctreffs Feb 13, 2026
b233795
Reset original benchmark code
ctreffs Feb 13, 2026
284e992
Remove conductor files
ctreffs Feb 13, 2026
689f29e
Pre-commit lint-fix
ctreffs Feb 13, 2026
7113bf9
Restore api.
ctreffs Feb 13, 2026
cde2c27
Fix FamilyMemberContainer
ctreffs Feb 13, 2026
2840d92
Finalize
ctreffs Feb 13, 2026
62a8046
Re-add require one component
ctreffs Feb 13, 2026
0876af9
Restore require API
ctreffs Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .sourcery.yml

This file was deleted.

6 changes: 0 additions & 6 deletions .sourceryTests.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Benchmarks/Benchmarks/ECSBenchmark/Base.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Color: Component {
}

class ExampleSystem {
private let family: Family2<Position, Velocity>
private let family: Family<Position, Velocity>

init(nexus: Nexus) {
family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self)
Expand Down
15 changes: 3 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ DOCS_VERSION_PATH ?= main
# The full base path for hosting
HOSTING_BASE_PATH ?= $(REPO_NAME)/$(DOCS_VERSION_PATH)

.PHONY: setup generate-code lint lint-fix test test-coverage testReadme build-debug build-release docs docs-preview docs-generate docs-coverage docs-check-coverage docs-check-links preview-analysis-docs generate-docs-githubpages pre-commit clean clean-sourcery
.PHONY: setup lint lint-fix test test-coverage testReadme build-debug build-release docs docs-preview docs-generate docs-coverage docs-check-coverage docs-check-links preview-analysis-docs generate-docs-githubpages pre-commit clean

# --- Setup ---

Expand All @@ -26,12 +26,6 @@ setup:
mint bootstrap
swift package resolve $(SWIFT_FLAGS)

# --- Codegen ---

generate-code:
mint run sourcery --quiet --config ./.sourcery.yml
mint run sourcery --quiet --config ./.sourceryTests.yml

# --- Quality Assurance ---

lint:
Expand Down Expand Up @@ -130,10 +124,7 @@ pre-commit: lint-fix test

# --- Cleanup ---

clean: clean-sourcery
clean:
swift package clean
rm -rdf .build
rm -rdf .swiftpm

clean-sourcery:
rm -rdf ${HOME}/Library/Caches/Sourcery
rm -rdf .swiftpm
1 change: 0 additions & 1 deletion Mintfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
realm/SwiftLint@0.63.2
nicklockwood/SwiftFormat@0.59.1
krzysztofzablocki/Sourcery@2.3.0
ldomaradzki/xcsift@v1.1.3
8 changes: 6 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import PackageDescription

let package = Package(
name: "FirebladeECS",
platforms: [
.macOS(.v14),
.iOS(.v17),
.tvOS(.v17),
.watchOS(.v10)
],
products: [
.library(name: "FirebladeECS",
targets: ["FirebladeECS"])
Expand All @@ -12,11 +18,9 @@ let package = Package(
],
targets: [
.target(name: "FirebladeECS",
exclude: ["Stencils/Family.stencil"],
swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]),
.testTarget(name: "FirebladeECSTests",
dependencies: ["FirebladeECS"],
exclude: ["Stencils/FamilyTests.stencil"],
swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]),
.testTarget(name: "FirebladeECSPerformanceTests",
dependencies: ["FirebladeECS"],
Expand Down
29 changes: 0 additions & 29 deletions Sources/FirebladeECS/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,45 +52,16 @@ For a more detailed example of FirebladeECS in action, see the [Fireblade ECS De
- ``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

Expand Down
29 changes: 5 additions & 24 deletions Sources/FirebladeECS/Entity+Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,14 @@ extension Entity {
nexus.get(safe: identifier)
}

/// Retrieves two components of the specified types assigned to this entity.
/// Retrieves components of the specified types assigned to this entity.
/// - Parameters:
/// - _: The first component type.
/// - _: The second component type.
/// - Returns: A tuple containing the component instances (or `nil` if not found).
/// - components: The component types to retrieve.
/// - Returns: A tuple containing the optional component instances.
/// - Complexity: O(1)
@inlinable
public func get<A, B>(components _: A.Type, _: B.Type) -> (A?, B?) where A: Component, B: Component {
let compA: A? = get(component: A.self)
let compB: B? = get(component: B.self)
return (compA, compB)
}

// swiftlint:disable large_tuple
/// Retrieves three components of the specified types assigned to this entity.
/// - Parameters:
/// - _: The first component type.
/// - _: The second component type.
/// - _: The third component type.
/// - Returns: A tuple containing the component instances (or `nil` if not found).
/// - Complexity: O(1)
@inlinable
public func get<A, B, C>(components _: A.Type, _: B.Type, _: C.Type) -> (A?, B?, C?) where A: Component, B: Component, C: Component {
let compA: A? = get(component: A.self)
let compB: B? = get(component: B.self)
let compC: C? = get(component: C.self)
return (compA, compB, compC)
public func get<each C: Component>(components: repeat (each C).Type) -> (repeat (each C)?) {
(repeat get(component: (each C).self))
}

/// Get or set component instance by type via subscript.
Expand Down
10 changes: 5 additions & 5 deletions Sources/FirebladeECS/Entity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public struct Entity {
/// - Returns: The created entity.
/// - Complexity: O(C + M) where C is the number of components and M is the number of families.
@discardableResult
public func createEntity(with components: Component...) -> Entity {
createEntity(with: components)
public func createEntity<each C: Component>(with components: repeat each C) -> Entity {
nexus.createEntity(with: repeat each components)
}

/// Creates a new entity with the provided components.
Expand Down Expand Up @@ -86,12 +86,12 @@ public struct Entity {
/// - Parameter components: one or more components.
/// - Complexity: O(M) where M is the number of families.
@discardableResult
public func assign(_ components: Component...) -> Entity {
assign(components)
public func assign<each C: Component>(_ components: repeat each C) -> Entity {
nexus.assign(components: repeat each components, to: self)
return self
}

/// Add a component to this entity.
/// Add a single component to this entity.
/// - Parameter component: a component.
/// - Complexity: O(M) where M is the number of families.
@discardableResult
Expand Down
45 changes: 45 additions & 0 deletions Sources/FirebladeECS/Family+Coding+Foundation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Family+Coding+Foundation.swift
// FirebladeECS
//
// Created by Christian Treffs on 13.02.26.
//

#if canImport(Foundation)
import Foundation

extension Family where repeat each C: Encodable {
/// Encode family members (entities) to data using a given encoder.
///
/// The encoded members will *NOT* be removed from the nexus and will also stay present in this family.
/// - Parameter encoder: The data encoder. Data encoder respects the coding strategy set at `nexus.codingStrategy`.
/// - Returns: The encoded data.
/// - Complexity: O(N) where N is the number of family members.
public func encodeMembers(using encoder: inout JSONEncoder) throws -> Data {
encoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] = nexus.codingStrategy
let container = FamilyMemberContainer<repeat each C>(components: makeIterator())
return try encoder.encode(container)
}
}

extension Family where repeat each C: Decodable {
/// Decode family members (entities) from given data using a decoder.
///
/// The decoded members will be added to the nexus and will be present in this family.
/// - Parameters:
/// - data: The data decoded by decoder. An unkeyed container of family members (keyed component containers) is expected.
/// - decoder: The decoder to use for decoding family member data. Decoder respects the coding strategy set at `nexus.codingStrategy`.
/// - Returns: returns the newly added entities.
/// - Complexity: O(N) where N is the number of family members in the data.
@discardableResult
public func decodeMembers(from data: Data, using decoder: inout JSONDecoder) throws -> [Entity] {
decoder.userInfo[CodingUserInfoKey.nexusCodingStrategy] = nexus.codingStrategy
let familyMembers = try decoder.decode(FamilyMemberContainer<repeat each C>.self, from: data)
return familyMembers.components
.map { (memberComponents: (repeat each C)) in
createMember(with: repeat each memberComponents)
}
}
}

#endif
Loading
Loading