Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better query syntax, observers, performance improvements #101

Merged
merged 51 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0970444
Start laying things out for better query syntax
0ffz Feb 17, 2024
041c1be
Fiddle with syntax for making systems, try out a DSL pattern, start i…
0ffz Feb 21, 2024
ac5376c
Lots more impl for listeners/checking listeners
0ffz Feb 21, 2024
ab29f6d
Fixing tests for new syntax
0ffz Feb 22, 2024
3c4dd24
Use `this` instead of target for referencing current queried entity
0ffz Feb 22, 2024
37575ed
Update more tests and usage to new syntax
0ffz Feb 24, 2024
7074061
Compiles again
0ffz Feb 24, 2024
b7bf7f2
All tests passing
0ffz Feb 24, 2024
30f6dfa
Disallow modification during system iteration if accessors use cache,…
0ffz Feb 25, 2024
918b854
Some cleanup for systems
0ffz Feb 25, 2024
06ff116
Fixes for orDefault and map accessors
0ffz Feb 25, 2024
9b4a5c4
Cleaning out some TODOs
0ffz Feb 28, 2024
ff30c0a
Some more todos!
0ffz Feb 28, 2024
018b0b7
Version bump
0ffz Mar 13, 2024
f56659d
Update autoscanner to work on new system function syntax
0ffz Mar 13, 2024
604933b
Feat: System names that get printed on error, along with a reasonable…
0ffz Mar 14, 2024
0f88990
Fix: ComponentOrDefaultAccessor incorrectly reading query.row instead…
0ffz Mar 15, 2024
42a418c
chore: Update publish action
0ffz Mar 17, 2024
0d2c8ae
docs: Update readme to latest syntax
0ffz Mar 17, 2024
4b04c7f
feat: Experimental query collection as a Kotlin Sequence
0ffz Mar 17, 2024
defec11
perf: Use fastForEach where possible
0ffz Mar 18, 2024
76c156b
perf: Avoid Record memory allocs, BucketULongArray to avoid array.cop…
0ffz Mar 21, 2024
123e06a
fix: Event runner not updating entity archetype/row information after…
0ffz Mar 22, 2024
03008f2
revert: Breaking change role bits being const
0ffz Mar 23, 2024
4763b58
feat: Component remove event
0ffz Mar 23, 2024
ba09022
test: Add test for adding other components to entity type while a rem…
0ffz Mar 23, 2024
d35be6f
fix: Missed case for component modified (after being set) event for e…
0ffz Mar 23, 2024
77a247a
fix: Avoid breaking changes with remove component event by adding mis…
0ffz Mar 24, 2024
ffebadb
fix: InnerSerializer not working for primitive descriptors
0ffz Mar 27, 2024
1ab06be
feat: instances component for creating prefab instances in config
0ffz Mar 28, 2024
0c53e6d
fix: Don't extend instances component right away, mark with an Inheri…
0ffz Mar 30, 2024
683afb4
feat: Log prefab path when it has malformed components
0ffz Apr 1, 2024
4387f21
perf: Mark all events with KeepArchetype by default
0ffz Apr 7, 2024
f33db2c
tests: Fix serializer tests
0ffz Apr 10, 2024
dcde5c8
perf: Avoid componentId calls in Entity methods checking InstanceOf
0ffz Apr 10, 2024
867db5d
Rework Listener system inspired by Flecs' observers (#103)
0ffz Apr 25, 2024
3398cd9
feat: OnFirstSet event
0ffz Apr 26, 2024
a13420a
chore: Cleanup PrefabLoader error messages
0ffz Apr 28, 2024
c60b114
refactor: Organize some packages in core, move Persists component to …
0ffz Apr 28, 2024
42cdd76
feat(observers): Entity scoped observers via a relation on entity
0ffz Apr 28, 2024
16aceba
feat(prefabs): Add using option to EventBind, backed by ReEmitEvent
0ffz Apr 30, 2024
989d435
fix(observers): Entity scoped observers not restricting by event type…
0ffz Apr 30, 2024
f5d9f29
fix(prefabs): Lots of incorrect behaviour with EntityObservers
0ffz Apr 30, 2024
f7fddf7
chore: Bump idofront (JVM 21)
0ffz May 2, 2024
9a15265
chore: Github workflow, build with jvm 21
0ffz May 2, 2024
9db7402
refactor: Swap atomifcfu for touchlab/Stately due to JVM 21 issues
0ffz May 2, 2024
11926ad
chore: Bump ido to avoid reobfJar task
0ffz May 2, 2024
7c8968b
chore: Add a few more common component names
0ffz May 13, 2024
2f77116
chore: Update idofront, kotlin 2.0.0, other deps
0ffz May 31, 2024
578d72e
chore: Consistently use T.(T) for supporting both shorthand and objec…
0ffz May 31, 2024
8e1100e
chore: Update readme
0ffz May 31, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/gradle-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
java-version: 21
cache: gradle

- name: Build
Expand Down
23 changes: 4 additions & 19 deletions .github/workflows/publish-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,13 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: MineInAbyss/publish-action@master
- uses: MineInAbyss/publish-action@develop
with:
maven-metadata-url: https://repo.mineinabyss.com/releases/com/mineinabyss/geary-core/maven-metadata.xml
maven-snapshot-metadata-url: https://repo.mineinabyss.com/snapshots/com/mineinabyss/geary-core/maven-metadata.xml
# pages-path: build/dokka/htmlMultiModule/
# dokka: dokkaHtmlMultiModule
maven-username: ${{ secrets.MAVEN_PUBLISH_USERNAME }}
maven-password: ${{ secrets.MAVEN_PUBLISH_PASSWORD }}

# deploy:
# permissions:
# pages: write
# id-token: write
# environment:
# name: github-pages
# url: ${{ steps.deployment.outputs.page_url }}
# if: ${{ github.ref == 'refs/heads/master' }}
# runs-on: ubuntu-latest
# needs: build
# steps:
# - name: Deploy to GitHub Pages
# id: deployment
# uses: actions/deploy-pages@v2
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build
target
out
*/out/production/
.kotlin

.classpath
.project
Expand All @@ -12,3 +13,5 @@ eclipse
*.ipr
*.iws
kotlin-js-store/

geary-benchmarks/.results
42 changes: 22 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,65 @@
<div align="center">

# Geary
[![Java CI with Gradle](https://github.com/MineInAbyss/Geary/actions/workflows/gradle-ci.yml/badge.svg)](https://github.com/MineInAbyss/Geary/actions/workflows/gradle-ci.yml)
[![Package](https://img.shields.io/maven-metadata/v?metadataUrl=https://repo.mineinabyss.com/releases/com/mineinabyss/geary-core/maven-metadata.xml)](https://repo.mineinabyss.com/#/releases/com/mineinabyss/geary-core)
[![Package](https://img.shields.io/maven-metadata/v?metadataUrl=https://repo.mineinabyss.com/releases/com/mineinabyss/geary-core/maven-metadata.xml&color=light_green)](https://repo.mineinabyss.com/#/releases/com/mineinabyss/geary-core)
[![Package](https://img.shields.io/maven-metadata/v?metadataUrl=https://repo.mineinabyss.com/snapshots/com/mineinabyss/geary-core/maven-metadata.xml&label=prerelease)](https://repo.mineinabyss.com/#/snapshots/com/mineinabyss/geary-core)
[![Wiki](https://img.shields.io/badge/-Project%20Wiki-blueviolet?logo=Wikipedia&labelColor=gray)](https://wiki.mineinabyss.com/geary)
[![Contribute](https://shields.io/badge/Contribute-e57be5?logo=github%20sponsors&style=flat&logoColor=white)](https://wiki.mineinabyss.com/contribute)
</div>

## Overview

Geary is an Entity Component System (ECS) written in Kotlin. The engine design is inspired by [flecs](https://github.com/SanderMertens/flecs). Core parts of the engine (ex. system iteration, entity creation) are quite optimized, with the main exception being our event system. We use Geary internally for our Minecraft plugins, see [geary-papermc](https://github.com/MineInAbyss/geary-papermc) for more info.
Geary is an Entity Component System (ECS) written in Kotlin. The engine design is inspired by [flecs](https://github.com/SanderMertens/flecs). Core parts of the engine like system iteration and entity creation are quite optimized, the main exception being our observer system. We use Geary internally for our Minecraft plugins, see [geary-papermc](https://github.com/MineInAbyss/geary-papermc) for more info.

## Features
- Archetype based engine optimized for many entities with similar components
- Type safe systems, queries, and event listeners
- Null safe component access
- Flecs-style entity relationships `alice.addRelation<FriendsWith>(bob)`
- Fully type safe system definition
- Observers for listening to component changes and custom events
- Prefabs that reuse components across entities
- Persistent components and loading prefabs from files thanks to [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization/)
- Addon system to use only what you need

## Example
## Usage

Read our [Quickstart guide](https://wiki.mineinabyss.com/geary/quickstart/) to see Geary in action, here's an excerpt, a simple velocity system:


A simple ssytem that iterates over all entities with a position and velocity, updating the position every engine tick.
```kotlin
data class Position(var x: Double, var y: Double)
data class Velocity(var x: Double, var y: Double)

class UpdatePositionSystem : TickingSystem(interval = 20.milliseconds) {
// Specify all components we want (Geary also supports branched AND/OR/NOT statements for selection)
val Pointer.position by get<Position>()
val Pointer.velocity by get<Velocity>()

override fun Pointer.tick() {
fun GearyModule.updatePositionSystem() = system(query<Position, Velocity>())
.every(interval = 20.milliseconds)
.exec { (position, velocity) ->
// We can access our components like regular variables!
position.x += velocity.x
position.y += velocity.y
}
}


fun main() {
// Set up geary
geary(ArchetypeEngineModule) {
// configure engine here
// example engine configuration
install(Prefabs)
}

geary.pipeline.addSystem(UpdatePositionSystem())
val posSystem = geary.updatePositionSystem()

// Create an entity the system can run on
entity {
setAll(Position(0.0, 0.0), Velocity(1.0, 0.0))
}

posSystem.tick() // exec just this system
geary.engine.tick() // exec all registered repeating systems, interval used to calculate every n ticks to run

val positions: List<Position> = posSystem.map { (pos) -> pos }
}

```
## Usage

A WIP wiki can be found at [wiki.mineinabyss.com](https://wiki.mineinabyss.com/geary/)

### Gradle
```kotlin
Expand All @@ -76,5 +79,4 @@ dependencies {
As the project matures, our primary goal is to make it useful to more people. Here are a handful of features we hope to achieve:
- Multiplatform support, with js, jvm, and native targets
- Publish numbers for benchmarks and cover more parts of the engine with them
- Component data migrations
- Complex queries (including relations like parent/child)
- Relation queries, ex. entities with a parent that has X component.
4 changes: 2 additions & 2 deletions addons/geary-autoscan/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ plugins {
}

dependencies {
compileOnly(project(":geary-core"))
compileOnly(project(":geary-serialization"))
implementation(project(":geary-core"))
implementation(project(":geary-serialization"))

implementation(idofrontLibs.reflections)
implementation(idofrontLibs.kotlin.reflect)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,28 @@ import co.touchlab.kermit.Severity
import com.mineinabyss.geary.addons.GearyPhase
import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault
import com.mineinabyss.geary.modules.geary
import com.mineinabyss.geary.systems.System
import com.mineinabyss.idofront.di.DI
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
import kotlin.reflect.KFunction

val autoScanner by DI.observe<AutoScanner>()

interface AutoScanner {
val scannedComponents: MutableSet<KClass<*>>
val scannedSystems: MutableSet<KClass<*>>
val scannedSystems: MutableSet<KFunction<*>>

fun installSystems()

companion object Addon : GearyAddonWithDefault<AutoScanner> {
override fun default() = object : AutoScanner {
private val logger get() = geary.logger
override val scannedComponents = mutableSetOf<KClass<*>>()
override val scannedSystems = mutableSetOf<KClass<*>>()
override val scannedSystems = mutableSetOf<KFunction<*>>()

override fun installSystems() {
scannedSystems.asSequence()
.mapNotNull { it.objectInstance ?: runCatching { it.createInstance() }.getOrNull() }
.filterIsInstance<System>()
.onEach { geary.pipeline.addSystem(it) }
.map { it::class.simpleName }
.onEach { it.call(geary) }
.map { it.name }
.let {
if (logger.config.minSeverity <= Severity.Verbose)
logger.i("Autoscan loaded singleton systems: ${it.joinToString()}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import co.touchlab.kermit.Severity
import com.mineinabyss.geary.addons.dsl.GearyDSL
import com.mineinabyss.geary.datatypes.Component
import com.mineinabyss.geary.modules.GearyConfiguration
import com.mineinabyss.geary.modules.GearyModule
import com.mineinabyss.geary.modules.geary
import com.mineinabyss.geary.serialization.dsl.serialization
import com.mineinabyss.geary.systems.System
import kotlinx.serialization.*
import kotlinx.serialization.modules.polymorphic
import org.reflections.Reflections
Expand All @@ -15,7 +15,8 @@ import org.reflections.util.ConfigurationBuilder
import org.reflections.util.FilterBuilder
import kotlin.reflect.KClass
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.jvm.kotlinFunction
import kotlin.reflect.typeOf

@GearyDSL
fun GearyConfiguration.autoscan(
Expand All @@ -37,6 +38,7 @@ class AutoScannerDSL(
ConfigurationBuilder()
.apply { limitTo.forEach { forPackage(it, classLoader) } }
.filterInputsBy(FilterBuilder().apply { limitTo.forEach { includePackage(it) } })
.setScanners(Scanners.SubTypes, Scanners.TypesAnnotated, Scanners.MethodsAnnotated)
)
}

Expand Down Expand Up @@ -96,10 +98,9 @@ class AutoScannerDSL(
*/
fun systems() {
val scanned = reflections
.get(Scanners.TypesAnnotated.with(AutoScan::class.java).asClass<Class<*>>(classLoader))
.asSequence()
.map { it.kotlin }
.filter { !it.hasAnnotation<ExcludeAutoScan>() && it.isSubclassOf(System::class) }
.get(Scanners.MethodsAnnotated.with(AutoScan::class.java))
.mapNotNull { reflections.forMethod(it, classLoader)?.kotlinFunction }
.filter { it.parameters.singleOrNull()?.type == typeOf<GearyModule>() }

autoScanner.scannedSystems += scanned
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.mineinabyss.geary.autoscan

import com.mineinabyss.geary.observers.Observer
import com.mineinabyss.geary.systems.GearySystem
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.RepeatingSystem
import com.mineinabyss.geary.systems.query.GearyQuery

/**
Expand All @@ -12,7 +11,7 @@ import com.mineinabyss.geary.systems.query.GearyQuery
annotation class ExcludeAutoScan

/**
* Indicates this [GearySystem], such as [RepeatingSystem], [Listener], or [GearyQuery] be registered automatically
* Indicates this [GearySystem], such as [RepeatingSystem], [Observer], or [GearyQuery] be registered automatically
* on startup by the AutoScanner.
*/
annotation class AutoScan
12 changes: 3 additions & 9 deletions addons/geary-prefabs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,13 @@ plugins {
}

kotlin {
targets {

}
sourceSets {
val commonMain by getting {
dependencies {
compileOnly(libs.okio)
compileOnly(libs.uuid)
compileOnly(idofrontLibs.kotlinx.serialization.json)
compileOnly(idofrontLibs.kotlinx.serialization.cbor)
implementation(project(":geary-core"))
implementation(project(":geary-serialization"))

compileOnly(project(":geary-core"))
compileOnly(project(":geary-serialization"))
implementation(libs.uuid)
implementation(idofrontLibs.idofront.di)
}
}
Expand Down
Loading
Loading