Skip to content

refactor: system init + add new search modifiers#875

Merged
rmrt1n merged 9 commits intomainfrom
ryan/search
Mar 22, 2026
Merged

refactor: system init + add new search modifiers#875
rmrt1n merged 9 commits intomainfrom
ryan/search

Conversation

@rmrt1n
Copy link
Contributor

@rmrt1n rmrt1n commented Mar 12, 2026

TL;DR

Better system init abstraction (yet again..) and new component search modifiers.

Component search modifiers

I see a lot of helper functions in rampage to filter over the results of Iter, which IMO isn't that great as it makes understanding what a system does harder as you have to jump between different functions. These modifiers hope to fix this by allowing you to filter on the spot so you don't lose context:

Filter results

Filter returns only the entities that matches your predicate:

func IsAlivePlayer(_ cardinal.EntityID, player PlayerSearch) bool {
    return player.HP.Get() > 0
}

for eid, player := range state.Players.Iter().Filter(IsAlivePlayer) {
    // Process only results that match your predicate...
}

Limit results

Limit returns only the first N results.

for eid, player := range state.Players.Iter().Limit(100) {
    // Process results...
}

Return a single element

Single returns a single result. It returns an error if the result is empty or has more than one element. This is a shorthand for finding singleton components. If you want to find the first result of a component that exists in multiple entities, use Limit(1).

eid, leaderboard, err := state.Leaderboard.Iter().Single()
if err != nil {
    // Handle the error
}
// Use the result...

Chaining modifiers

You can chain these functions together for complex searches, e.g.

// Find a single entity that matches the search:
_, account, err := state.Accounts.Iter().
    Filter(func (_ cardinal.EntityID, a AccountSearch) bool {
        return a.AccountData.Get().Persona == persona
    }).
    Single()
assert.That(err == nil)

// Chain multiple filters:
results := state.Players.Iter().Filter(IsAlivePlayer).Filter(HasBuff).Filter(HasPet)
for eid, player := range results {
    // Process only results that match your filters...
}

Internal changes

This PR includes several improvements:

  • Moved all system field init to pkg/cardinal/system.go.
    This consolidates everything in one place. I don't know why it took me so long to realize this. The way to go is to just expose ECS APIs and call them from the system fields. It's much cleaner now and less confusing.
  • Performance improvements to the system event manager.
    Nothing too special here. The existing version does a lot of boxing because the containers hold interfaces. Replaced it with the same approach as the archetype columns.

Copy link
Contributor Author

rmrt1n commented Mar 12, 2026


How to use the Graphite Merge Queue

Add the label graphite/merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@rmrt1n rmrt1n marked this pull request as ready for review March 12, 2026 10:14
@rmrt1n rmrt1n requested a review from smsunarto March 12, 2026 10:15
@rmrt1n rmrt1n requested a review from winton-library March 16, 2026 08:32
@rmrt1n rmrt1n merged commit 955e749 into main Mar 22, 2026
8 checks passed
@rmrt1n rmrt1n deleted the ryan/search branch March 22, 2026 11:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants