From cc338ee476b3f01325a4e82effb774d2652a949f Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sat, 17 Feb 2024 11:27:37 -0500 Subject: [PATCH 01/14] Start laying things out for better query syntax --- .../systems/accessors/AccessorOperations.kt | 9 +-- .../geary/systems/accessors/Aliases.kt | 5 +- .../accessors/type/ComponentAccessor.kt | 5 +- .../type/ComponentOrDefaultAccessor.kt | 22 ++++--- .../geary/systems/query/CachedQueryRunner.kt | 35 ++++++++++ .../geary/systems/query/EventQuery.kt | 6 ++ .../geary/systems/query/NewQuery.kt | 53 +++++++++++++++ .../geary/systems/query/QueriedEntity.kt | 7 ++ .../mineinabyss/geary/systems/query/Query.kt | 65 ++++--------------- .../geary/events/CheckingListenerTest.kt | 1 + .../geary/events/ComponentAddEventTest.kt | 2 +- 11 files changed, 138 insertions(+), 72 deletions(-) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 6c17fafbe..98e60001b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -7,16 +7,17 @@ import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.systems.accessors.type.* +import com.mineinabyss.geary.systems.query.QueriedEntity import kotlin.reflect.KProperty open class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ - inline fun get(): ComponentAccessor { + inline fun QueriedEntity.get(): ComponentAccessor { return NonNullComponentAccessor(componentId().withRole(HOLDS_DATA)) } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ - inline fun getRelation(): ComponentAccessor { + inline fun QueriedEntity.getRelation(): ComponentAccessor { return NonNullComponentAccessor(Relation.of().id) } @@ -65,12 +66,12 @@ open class AccessorOperations { * - One of [K] or [T] is [Any] => gets all relations matching the other (specified) type. * - Note: nullability rules are still upheld with [Any]. */ - inline fun getRelations(): RelationsAccessor { + inline fun QueriedEntity.getRelations(): RelationsAccessor { return RelationsAccessor(componentIdWithNullable(), componentIdWithNullable()) } /** @see getRelations */ - inline fun getRelationsWithData(): RelationsWithDataAccessor { + inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { return RelationsWithDataAccessor(componentIdWithNullable(), componentIdWithNullable()) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt index 9c9250725..c08dc171f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt @@ -2,12 +2,13 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.RecordPointer import com.mineinabyss.geary.datatypes.Records +import com.mineinabyss.geary.systems.query.Query import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty -typealias ReadOnlyAccessor = ReadOnlyProperty -typealias ReadWriteAccessor = ReadWriteProperty +typealias ReadOnlyAccessor = ReadOnlyProperty +typealias ReadWriteAccessor = ReadWriteProperty /** A pointer to where a specific entity's data is stored for use by accessors. */ typealias Pointer = RecordPointer diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 9c0e2dc74..21ef009a8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -8,6 +8,7 @@ import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.accessors.ReadWriteAccessor +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) @@ -46,11 +47,11 @@ abstract class ComponentAccessor( cachedDataArray[thisRef.row] = value } - final override fun getValue(thisRef: Pointer, property: KProperty<*>): T { + final override fun getValue(thisRef: Query, property: KProperty<*>): T { return get(thisRef) } - final override fun setValue(thisRef: Pointer, property: KProperty<*>, value: T) { + final override fun setValue(thisRef: Query, property: KProperty<*>, value: T) { return set(thisRef, value) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt index 15f61048b..a5f1f24d0 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt @@ -3,8 +3,8 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) @@ -15,13 +15,17 @@ class ComponentOrDefaultAccessor( private var cachedIndex = -1 private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: Pointer, property: KProperty<*>): T { - val archetype = thisRef.archetype - if (archetype !== cachedArchetype) { - cachedArchetype = archetype - cachedIndex = archetype.indexOf(id) - } - if (cachedIndex == -1) return default() - return archetype.componentData[cachedIndex][thisRef.row] as T + override fun getValue(thisRef: Query, property: KProperty<*>): T { + TODO("Not yet implemented") } + +// override fun getValue(thisRef: Pointer, property: KProperty<*>): T { +// val archetype = thisRef.archetype +// if (archetype !== cachedArchetype) { +// cachedArchetype = archetype +// cachedIndex = archetype.indexOf(id) +// } +// if (cachedIndex == -1) return default() +// return archetype.componentData[cachedIndex][thisRef.row] as T +// } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt new file mode 100644 index 000000000..9b6d23638 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -0,0 +1,35 @@ +package com.mineinabyss.geary.systems.query + +import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.helpers.fastForEachWithIndex + +class CachedQueryRunner(val query: T) { + val matchedArchetypes: MutableList = mutableListOf() + val family = query.buildQuery() + + inline fun toList(crossinline map: T.() -> R): List { + val list = mutableListOf() + forEach { list.add(this.map()) } + return list + } + + /** + * Quickly iterates over all matched entities, running [run] for each. + * + * Use [apply] on the query to use its accessors. + * */ + inline fun forEach(crossinline run: T.() -> Unit) { + val matched = matchedArchetypes + matched.fastForEachWithIndex { i, archetype -> + archetype.isIterating = true + val upTo = archetype.size + // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration + query.currArchetype = archetype + for (entityIndex in 0 until upTo) { + query.currEntityIndex = entityIndex + run(query) + } + archetype.isIterating = false + } + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt new file mode 100644 index 000000000..a49d4c91f --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt @@ -0,0 +1,6 @@ +package com.mineinabyss.geary.systems.query + +abstract class EventQuery() : Query() { + val event: QueriedEntity = TODO() + val source: QueriedEntity = TODO() +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt new file mode 100644 index 000000000..82b7491ae --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt @@ -0,0 +1,53 @@ +package com.mineinabyss.geary.systems.query + +import kotlin.time.Duration + + +inline fun system( + query: () -> T, +// interval: Duration = Duration.ZERO, + onTick: T.() -> Unit, +): System { +// onTick(query) +} + +class SystemTick { + inline fun forEach(run: T.() -> Unit) { + + } +} + +class SystemBuilder(val query: T) { + inline fun onTick(run: T.() -> Unit): SystemBuilder { + SystemTick().forEach(run) + return this + } + + var interval: Duration = Duration.ZERO +} + +class System + +class TestQuery : EventQuery() { + var string by target.get() + val int by event.get().map { it.toString() } +} + + +fun printStringSystem() = system(fun() = object : Query() { + var string by target.get() +}) { + println(string) +} + +fun main() { + system(::TestQuery) { + println(string) + } + + system(fun() = object : Query() { + var string by target.get().removable() + }) { + println(string) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt new file mode 100644 index 000000000..1c77b1891 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -0,0 +1,7 @@ +package com.mineinabyss.geary.systems.query + +import com.mineinabyss.geary.datatypes.EntityId + +abstract class QueriedEntity { + val entity: EntityId = TODO() +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index 00972c623..84c9b8cfb 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -1,67 +1,24 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.helpers.fastForEachWithIndex -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.AccessorHolder +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching -import com.mineinabyss.geary.systems.accessors.Pointer -import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty -/**com.mineinabyss.geary.ecs.engine.iteration.accessors - * @property matchedArchetypes A set of archetypes which have been matched to this query. - */ -abstract class Query : AccessorHolder() { +abstract class Query : AccessorOperations() { @PublishedApi - internal val matchedArchetypes: MutableSet = mutableSetOf() - + internal var currArchetype: Archetype? = null @PublishedApi - internal var registered: Boolean = false - - val matchedEntities - get(): List { - registerIfNotRegistered() - return matchedArchetypes.flatMap { it.entities } - } - - fun registerIfNotRegistered() { - if (!registered) { - geary.queryManager.trackQuery(this) - } - } - - inline fun toList(crossinline map: (Pointer) -> T): List { - val list = mutableListOf() - forEach { list.add(map(it)) } - return list - } + internal var currEntityIndex: Int = 0 - /** - * Quickly iterates over all matched entities, running [run] for each. - * - * Use [apply] on the query to use its accessors. - * */ - inline fun forEach(crossinline run: (Pointer) -> Unit) { - registerIfNotRegistered() - val matched = matchedArchetypes.toList() - matched.fastForEachWithIndex { i, archetype -> - archetype.isIterating = true - val upTo = archetype.size - // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration - for (entityIndex in 0 until upTo) { - run(Pointer(archetype, entityIndex)) - } - archetype.isIterating = false - } - } + val target: QueriedEntity = TODO() - operator fun Family.provideDelegate(thisRef: GearyQuery, property: KProperty<*>): ReadOnlyProperty { - mutableFamily.add(this) - return ReadOnlyProperty { thisRef, prop -> - this@provideDelegate + private val props: MutableMap, Family> = mutableMapOf() + fun buildQuery(): Family = family { + for (family in props.values) { + add(family) } } @@ -70,7 +27,7 @@ abstract class Query : AccessorHolder() { thisRef: Any, prop: KProperty<*> ): T { - family?.let { mutableFamily.add(it) } + family?.let { props[prop] = it } return this } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt index ff61b1385..9df23b19d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt @@ -5,6 +5,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe import kotlin.test.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index 1952bc690..587a8dad8 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -27,7 +27,7 @@ internal class ComponentAddEventTest : GearyTest() { @Test fun componentAddEvent() { - val listener = OnStringAdd() + val listener = listener(StringAddQuery()).handle { inc++ } geary.pipeline.addSystem(listener) entity { From 565e86cdb27c9dc4081d588b782203770289faf4 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 21 Feb 2024 21:13:43 +0100 Subject: [PATCH 02/14] Fiddle with syntax for making systems, try out a DSL pattern, start implementing new query syntax for tracking --- .../benchmarks/VelocitySystemBenchmark.kt | 40 +++---------- .../geary/datatypes/GearyAliases.kt | 1 - .../geary/datatypes/RecordPointer.kt | 42 -------------- .../mineinabyss/geary/datatypes/Records.kt | 23 -------- .../geary/datatypes/family/Family.kt | 4 +- .../mineinabyss/geary/engine/QueryManager.kt | 4 +- .../archetypes/ArchetypeQueryManager.kt | 18 +++--- .../geary/modules/GearyConfiguration.kt | 6 +- .../mineinabyss/geary/modules/GearyModule.kt | 2 +- .../mineinabyss/geary/modules/dsl/QueryAPI.kt | 8 +++ .../geary/systems/RepeatingSystem.kt | 25 --------- .../com/mineinabyss/geary/systems/System.kt | 16 +++--- .../geary/systems/TrackedSystem.kt | 12 ++++ .../systems/accessors/AccessorOperations.kt | 7 ++- .../geary/systems/accessors/Aliases.kt | 12 ---- .../accessors/type/ComponentAccessor.kt | 18 +++--- .../type/NonNullComponentAccessor.kt | 11 ++-- .../geary/systems/query/CachedQueryRunner.kt | 9 +-- .../geary/systems/query/EventQuery.kt | 4 +- .../geary/systems/query/NewQuery.kt | 56 ++++++++++--------- .../geary/systems/query/QueriedEntity.kt | 14 ++++- .../mineinabyss/geary/systems/query/Query.kt | 14 ++--- .../{systems => queries}/SimpleQueryTest.kt | 16 +++--- 23 files changed, 144 insertions(+), 218 deletions(-) delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Records.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/dsl/QueryAPI.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/RepeatingSystem.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt rename geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/{systems => queries}/SimpleQueryTest.kt (63%) diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt index 34aba6c4d..ad8a6c0d8 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt @@ -7,7 +7,8 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.RepeatingSystem -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.query.system import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup @@ -18,34 +19,16 @@ class VelocitySystemBenchmark { data class Velocity(val x: Float, val y: Float) data class Position(var x: Float, var y: Float) - object VelocitySystem : RepeatingSystem() { - private val Pointer.velocity by get() - private var Pointer.position by get() - - override fun Pointer.tick() { + val VelocitySystem = geary.system(object : Query() { + val velocity by target.get() + var position by target.get() + }) { + onTick { position.x += velocity.x position.y += velocity.y } } - // sanity check that `by` and a function call is not causing slowdowns - object VelocitySystemNoBoxing : RepeatingSystem() { - private val velocity = get() - private var position = get() - - val test by family { - hasSet() - hasSet() - } - - override fun tickAll() { - forEach { - position[it].x += velocity[it].x - position[it].y += velocity[it].y - } - } - } - val velocities = Array(tenMil) { Velocity(it.toFloat() / oneMil, it.toFloat() / oneMil) } val positions = Array(tenMil) { Position(0f, 0f) } @@ -63,19 +46,14 @@ class VelocitySystemBenchmark { @Benchmark fun velocitySystem() { - VelocitySystem.tickAll() - } - - @Benchmark - fun velocitySystemNoBoxing() { - VelocitySystemNoBoxing.tickAll() + createVelocitySystem().tickAll() } // Theoretical performance with zero ECS overhead @Benchmark fun pureArrays() { var i = 0 - while(i < tenMil) { + while (i < tenMil) { positions[i].x += velocities[i].x positions[i].y += velocities[i].y i++ diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/GearyAliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/GearyAliases.kt index 8c9f6127d..1adb38e3c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/GearyAliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/GearyAliases.kt @@ -4,7 +4,6 @@ import kotlinx.serialization.Polymorphic typealias GearyEntityType = EntityType typealias GearyRecord = Record -typealias GearyRecords = Records typealias GearyRelation = Relation /** diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt deleted file mode 100644 index a1882bb3a..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.mineinabyss.geary.datatypes - -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.archetypes - -/** A record created in place that delegates to the real entity pointer the first time [entity] gets accessed. */ -class RecordPointer @PublishedApi internal constructor( - archetype: Archetype, - row: Int -) { - constructor(record: Record) : this(record.archetype, record.row) { - delegated = true - delegate = record - } - - private val originalArchetype = archetype - private val originalRow = row - - @UnsafeAccessors - val archetype: Archetype get() = if (delegated) delegate!!.archetype else originalArchetype - val row: Int get() = if (delegated) delegate!!.row else originalRow - - private var delegate: Record? = null - private var delegated = false - - @UnsafeAccessors - val entity: Entity - get() { - val entity = archetype.getEntity(row) - if (!delegated) { - delegate = archetypes.records[entity] - } - delegated = true - return entity - } - - @UnsafeAccessors - operator fun component1(): Archetype = archetype - - operator fun component2(): Int = row -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Records.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Records.kt deleted file mode 100644 index d53647f08..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Records.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.mineinabyss.geary.datatypes - -import com.mineinabyss.geary.systems.accessors.Pointer - -/** - * A collection of records used for queries involving multiple entities. - * - * Currently built for our event system but will support arbitrary entities once we improve the query system. - */ -class Records( - val target: Pointer, - val event: Pointer, - val source: Pointer?, -) { - fun getByIndex(index: Int): Pointer { - return when (index) { - 0 -> target - 1 -> event - 2 -> source ?: error("Source is null") - else -> error("Index out of bounds") - } - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt index 6df1d66dd..54e0f8373 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt @@ -2,9 +2,9 @@ package com.mineinabyss.geary.datatypes.family import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityId -import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty sealed interface Family : ReadOnlyAccessor, FamilyMatching { @@ -44,7 +44,7 @@ sealed interface Family : ReadOnlyAccessor, FamilyMatching { // Helpers for writing queries override val family: Family? get() = this - override fun getValue(thisRef: Pointer, property: KProperty<*>): Family { + override fun getValue(thisRef: Query, property: KProperty<*>): Family { return this } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt index 6bc9b4260..db033846b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt @@ -3,11 +3,13 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.systems.Listener +import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.GearyQuery +import com.mineinabyss.geary.systems.query.Query interface QueryManager { fun trackEventListener(listener: Listener) - fun trackQuery(query: GearyQuery) + fun trackQuery(query: T): CachedQueryRunner /** Returns a list of entities matching the given family. */ fun getEntitiesMatching(family: Family): List diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt index a3df536c3..7a60044b8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt @@ -6,13 +6,14 @@ import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap import com.mineinabyss.geary.engine.QueryManager import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.systems.Listener +import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.GearyQuery import com.mineinabyss.geary.systems.query.Query import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.synchronized class ArchetypeQueryManager : QueryManager { - private val queries = mutableListOf() + private val queries = mutableListOf>() private val sourceListeners = mutableListOf() private val targetListeners = mutableListOf() private val eventListeners = mutableListOf() @@ -22,7 +23,7 @@ class ArchetypeQueryManager : QueryManager { setIndex = { it, index -> it.id = index } ) - val archetypeRegistryLock = SynchronizedObject() + private val archetypeRegistryLock = SynchronizedObject() val archetypeCount get() = archetypes.elements.size @@ -46,11 +47,12 @@ class ArchetypeQueryManager : QueryManager { } } - override fun trackQuery(query: GearyQuery) { - val matched = archetypes.match(query.family) - query.matchedArchetypes += matched - queries.add(query) - query.registered = true + override fun trackQuery(query: T): CachedQueryRunner { + val queryRunner = CachedQueryRunner(query) + val matched = archetypes.match(queryRunner.family) + queryRunner.matchedArchetypes += matched + queries.add(queryRunner) + return queryRunner } internal fun registerArchetype(archetype: Archetype) = synchronized(archetypeRegistryLock) { @@ -71,7 +73,7 @@ class ArchetypeQueryManager : QueryManager { } data class MatchedQueries( - val queries: List, + val queries: List>, val sourceListeners: List, val targetListeners: List, val eventListeners: List diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt index c93c82cdc..df762c38a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt @@ -9,13 +9,15 @@ import com.mineinabyss.idofront.di.DI import kotlin.reflect.KClass @GearyDSL -class GearyConfiguration { +class GearyConfiguration( + val module: GearyModule +) { val installedAddons = mutableMapOf>, Any>() inline fun , reified Module : Any> install( addon: T, ): Module { val module = DI.getOrNull() - if(module != null) return module + if (module != null) return module return install(addon, addon.default()) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModule.kt index 63712e924..73ca1276f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModule.kt @@ -44,7 +44,7 @@ interface GearyModule { val defaults: Defaults operator fun invoke(configure: GearyConfiguration.() -> Unit) { - GearyConfiguration().apply(configure) + GearyConfiguration(this).apply(configure) } companion object diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/dsl/QueryAPI.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/dsl/QueryAPI.kt new file mode 100644 index 000000000..28828bfc3 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/dsl/QueryAPI.kt @@ -0,0 +1,8 @@ +package com.mineinabyss.geary.modules.dsl + +import com.mineinabyss.geary.modules.GearyConfiguration +import com.mineinabyss.geary.systems.query.Query + +fun GearyConfiguration.track(query: Query) { + +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/RepeatingSystem.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/RepeatingSystem.kt deleted file mode 100644 index cfbf0d887..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/RepeatingSystem.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.mineinabyss.geary.systems - -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer -import com.mineinabyss.geary.systems.query.Query -import kotlin.time.Duration - -/** - * #### [Guide: Ticking systems](https://wiki.mineinabyss.com/geary/guide/ticking-systems) - * - * A system for the ECS that will run every [interval] ticks. - * - * @param interval How often to run this system in ticks. - */ -abstract class RepeatingSystem( - val interval: Duration = geary.defaults.repeatingSystemInterval -) : Query(), System { - override fun onStart() {} - - open fun tickAll() { - forEach(run = { it.tick() }) - } - - protected open fun Pointer.tick() {} -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt index cefca98fb..aa26f4358 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt @@ -1,9 +1,11 @@ package com.mineinabyss.geary.systems -/** - * An interface representing all types of systems that can be registered with the engine. - * Includes [Listener] and [RepeatingSystem]. - */ -interface System { - fun onStart() {} -} +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query +import kotlin.time.Duration + +class System internal constructor( + val query: Query, + val onTick: CachedQueryRunner<*>.() -> Unit, + val interval: Duration, +) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt new file mode 100644 index 000000000..88989c0bd --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt @@ -0,0 +1,12 @@ +package com.mineinabyss.geary.systems + +import com.mineinabyss.geary.systems.query.CachedQueryRunner + +class TrackedSystem( + val system: System, + val runner: CachedQueryRunner<*> +) { + fun tick() { + system.onTick(runner) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 98e60001b..d7b924db3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -8,17 +8,18 @@ import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty open class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ inline fun QueriedEntity.get(): ComponentAccessor { - return NonNullComponentAccessor(componentId().withRole(HOLDS_DATA)) + return NonNullComponentAccessor(this, componentId().withRole(HOLDS_DATA)) } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ inline fun QueriedEntity.getRelation(): ComponentAccessor { - return NonNullComponentAccessor(Relation.of().id) + return NonNullComponentAccessor(this, Relation.of().id) } /** @@ -42,7 +43,7 @@ open class AccessorOperations { return object : ReadOnlyAccessor, FamilyMatching { override val family = (this@map as? FamilyMatching)?.family - override fun getValue(thisRef: Pointer, property: KProperty<*>): U { + override fun getValue(thisRef: Query, property: KProperty<*>): U { val value = this@map.getValue(thisRef, property) return mapping(value) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt index c08dc171f..de784618c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt @@ -1,20 +1,8 @@ package com.mineinabyss.geary.systems.accessors -import com.mineinabyss.geary.datatypes.RecordPointer -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.systems.query.Query import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty - typealias ReadOnlyAccessor = ReadOnlyProperty typealias ReadWriteAccessor = ReadWriteProperty - -/** A pointer to where a specific entity's data is stored for use by accessors. */ -typealias Pointer = RecordPointer - -/** A list of [Pointer]s, currently used to select between the target, source, and event entity in listeners. */ -typealias Pointers = Records - -typealias GearyPointer = RecordPointer -typealias GearyPointers = Records diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 21ef009a8..4f9e0f86a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -8,11 +8,13 @@ import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.accessors.ReadWriteAccessor +import com.mineinabyss.geary.systems.query.QueriedEntity import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) abstract class ComponentAccessor( + val entity: QueriedEntity, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { override val family: Family = family { hasSet(id) } @@ -21,30 +23,30 @@ abstract class ComponentAccessor( protected var cachedDataArray: MutableList = mutableListOf() protected var cachedArchetype: Archetype? = null - abstract operator fun get(thisRef: Pointer): T + abstract operator fun get(thisRef: Query): T - internal inline fun get(thisRef: Pointer, beforeRead: () -> Unit): T { - val archetype = thisRef.archetype + internal inline fun get(thisRef: Query, beforeRead: () -> Unit): T { + val archetype = entity.currArchetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeRead() - return cachedDataArray[thisRef.row] + return cachedDataArray[entity.currEntityIndex] } - abstract operator fun set(thisRef: Pointer, value: T) + abstract operator fun set(thisRef: Query, value: T) - internal inline fun set(thisRef: Pointer, value: T, beforeWrite: () -> Unit) { - val archetype = thisRef.archetype + internal inline fun set(thisRef: Query, value: T, beforeWrite: () -> Unit) { + val archetype = entity.currArchetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeWrite() - cachedDataArray[thisRef.row] = value + cachedDataArray[entity.currEntityIndex] = value } final override fun getValue(thisRef: Query, property: KProperty<*>): T { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt index 51a1c3dcd..b031ffca6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt @@ -3,18 +3,21 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query @OptIn(UnsafeAccessors::class) class NonNullComponentAccessor( + entity: QueriedEntity, id: ComponentId, -) : ComponentAccessor(id) { - override operator fun get(thisRef: Pointer): T = +) : ComponentAccessor(entity, id) { + override operator fun get(thisRef: Query): T = get(thisRef, beforeRead = {}) - override operator fun set(thisRef: Pointer, value: T) { + override operator fun set(thisRef: Query, value: T) { set(thisRef, value, beforeWrite = { if (cachedIndex == -1) { - thisRef.entity.set(value, id) + entity.entity.set(value, id) return } }) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index 9b6d23638..feb6ef2e7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -3,9 +3,9 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.helpers.fastForEachWithIndex -class CachedQueryRunner(val query: T) { +class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() - val family = query.buildQuery() + val family = query.buildFamily() inline fun toList(crossinline map: T.() -> R): List { val list = mutableListOf() @@ -23,10 +23,11 @@ class CachedQueryRunner(val query: T) { matched.fastForEachWithIndex { i, archetype -> archetype.isIterating = true val upTo = archetype.size + val target = query.target // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration - query.currArchetype = archetype + target.currArchetype = archetype for (entityIndex in 0 until upTo) { - query.currEntityIndex = entityIndex + target.currEntityIndex = entityIndex run(query) } archetype.isIterating = false diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt index a49d4c91f..3b2d72c40 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt @@ -1,6 +1,6 @@ package com.mineinabyss.geary.systems.query abstract class EventQuery() : Query() { - val event: QueriedEntity = TODO() - val source: QueriedEntity = TODO() + val event: QueriedEntity = QueriedEntity() + val source: QueriedEntity = QueriedEntity() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt index 82b7491ae..c3fe0db45 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt @@ -1,14 +1,18 @@ package com.mineinabyss.geary.systems.query +import com.mineinabyss.geary.modules.GearyConfiguration +import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.systems.System import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds -inline fun system( - query: () -> T, -// interval: Duration = Duration.ZERO, - onTick: T.() -> Unit, +inline fun GearyModule.system( + query: T, + init: SystemBuilder.() -> Unit ): System { -// onTick(query) + val system = SystemBuilder(query).apply(init).build() + return pipeline.addSystem(system) } class SystemTick { @@ -18,36 +22,38 @@ class SystemTick { } class SystemBuilder(val query: T) { - inline fun onTick(run: T.() -> Unit): SystemBuilder { - SystemTick().forEach(run) - return this - } - + @PublishedApi + internal var onTick: CachedQueryRunner.() -> Unit = {} var interval: Duration = Duration.ZERO -} - -class System -class TestQuery : EventQuery() { - var string by target.get() - val int by event.get().map { it.toString() } + inline fun onTick(crossinline run: T.() -> Unit) { + onTick = { forEach(run) } + } + fun onTickAll(run: CachedQueryRunner.() -> Unit) { + onTick = run + } + fun build(): System = System( + query, + onTick as CachedQueryRunner<*>.() -> Unit , + interval + ) } - -fun printStringSystem() = system(fun() = object : Query() { +fun printStringSystem() = system(object : Query() { var string by target.get() }) { - println(string) -} - -fun main() { - system(::TestQuery) { + onTick { println(string) } +} - system(fun() = object : Query() { +fun main() { + system(object : Query() { var string by target.get().removable() }) { - println(string) + onTick { + println(string) + } + interval = 2.seconds } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index 1c77b1891..6c2bbc9a7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -1,7 +1,17 @@ package com.mineinabyss.geary.systems.query +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.archetypes -abstract class QueriedEntity { - val entity: EntityId = TODO() +class QueriedEntity { + @PublishedApi + internal var currArchetype: Archetype = archetypes.archetypeProvider.rootArchetype + @PublishedApi + internal var currEntityIndex: Int = 0 + + @UnsafeAccessors + val entity: GearyEntity = TODO() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index 84c9b8cfb..373421f20 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -2,21 +2,19 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family -import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty -abstract class Query : AccessorOperations() { - @PublishedApi - internal var currArchetype: Archetype? = null - @PublishedApi - internal var currEntityIndex: Int = 0 +abstract class Query( + val baseFamily: Family = family { } +) : AccessorOperations() { - val target: QueriedEntity = TODO() + val target: QueriedEntity = QueriedEntity() private val props: MutableMap, Family> = mutableMapOf() - fun buildQuery(): Family = family { + + fun buildFamily(): Family = family { for (family in props.values) { add(family) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/SimpleQueryTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt similarity index 63% rename from geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/SimpleQueryTest.kt rename to geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt index 5b917745e..44a21711d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/SimpleQueryTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt @@ -1,19 +1,23 @@ -package com.mineinabyss.geary.systems +package com.mineinabyss.geary.queries import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.modules.dsl.track +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.query.system import io.kotest.matchers.shouldBe import kotlin.test.Test class SimpleQueryTest : GearyTest() { class MyQuery : Query() { - val Pointer.int by get() + val int by target.get() } @Test fun `simple query`() { + val query = geary.queryManager.trackQuery(MyQuery()) repeat(10) { entity { set(1) @@ -24,11 +28,9 @@ class SimpleQueryTest : GearyTest() { } var count = 0 - MyQuery().run { - forEach { - it.int shouldBe 1 - count++ - } + query.forEach { + int shouldBe 1 + count++ } count shouldBe 10 } From a8b3215e87f60010b548d54a43089c52ddc35fbf Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 22 Feb 2024 00:13:26 +0100 Subject: [PATCH 03/14] Lots more impl for listeners/checking listeners --- .../components/ChildrenOnPrefab.kt | 2 +- .../systems/ParseChildOnPrefab.kt | 2 +- .../systems/ParseRelationWithDataSystem.kt | 2 +- .../systems/TrackPrefabsByKeySystem.kt | 2 +- .../mineinabyss/geary/prefabs/PrefabTests.kt | 2 +- .../geary/prefabs/SerializerTest.kt | 2 +- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 20 +++-- .../benchmarks/VelocitySystemBenchmark.kt | 8 +- .../geary/datatypes/family/Family.kt | 9 +-- .../geary/datatypes/family/MutableFamily.kt | 2 +- .../com/mineinabyss/geary/engine/Pipeline.kt | 9 ++- .../mineinabyss/geary/engine/PipelineImpl.kt | 44 +++++----- .../mineinabyss/geary/engine/QueryManager.kt | 2 +- .../geary/engine/archetypes/Archetype.kt | 6 +- .../engine/archetypes/ArchetypeEngine.kt | 8 +- .../engine/archetypes/ArchetypeEventRunner.kt | 36 ++++----- .../archetypes/ArchetypeQueryManager.kt | 22 ++--- .../geary/events/CheckingListener.kt | 20 ----- .../mineinabyss/geary/systems/GearyAliases.kt | 3 +- .../com/mineinabyss/geary/systems/Listener.kt | 81 +++---------------- .../geary/systems/ListenerBuilder.kt | 58 +++++++++++++ .../{query/NewQuery.kt => SystemBuilder.kt} | 38 ++------- .../geary/systems/accessors/AccessorHolder.kt | 21 ----- .../systems/accessors/AccessorOperations.kt | 16 +++- .../geary/systems/accessors/Aliases.kt | 4 +- .../geary/systems/accessors/FamilyMatching.kt | 2 + .../ReadOnlyEntitySelectingAccessor.kt | 15 ---- .../ReadWriteEntitySelectingAccessor.kt | 15 ---- .../accessors/type/ComponentAccessor.kt | 25 +++--- .../type/ComponentOrDefaultAccessor.kt | 3 +- .../type/NonNullComponentAccessor.kt | 9 +-- .../accessors/type/RelationsAccessor.kt | 10 +-- .../type/RelationsWithDataAccessor.kt | 13 +-- .../type/RemovableComponentAccessor.kt | 16 ++-- .../geary/systems/query/CachedQueryRunner.kt | 4 +- .../geary/systems/query/EventQuery.kt | 67 ++++++++++++++- .../geary/systems/query/QueriedEntity.kt | 19 ++++- .../mineinabyss/geary/systems/query/Query.kt | 15 +--- .../geary/events/CheckingListenerTest.kt | 20 ++--- .../geary/events/ComponentAddEventTest.kt | 28 +++---- .../geary/events/SimpleEventTest.kt | 53 ++++++------ .../geary/queries/SimpleQueryTest.kt | 3 - 42 files changed, 352 insertions(+), 384 deletions(-) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt rename geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/{query/NewQuery.kt => SystemBuilder.kt} (50%) delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolder.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadOnlyEntitySelectingAccessor.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadWriteEntitySelectingAccessor.kt diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/ChildrenOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/ChildrenOnPrefab.kt index 287b50dda..222df7d7f 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/ChildrenOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/ChildrenOnPrefab.kt @@ -2,8 +2,8 @@ package com.mineinabyss.geary.prefabs.configuration.components import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.datatypes.Component -import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.InnerSerializer +import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import kotlinx.serialization.Polymorphic import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.Serializable diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt index 2e248e500..9b62221b1 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt @@ -1,8 +1,8 @@ package com.mineinabyss.geary.prefabs.configuration.systems +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.components.relations.NoInherit -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.helpers.addParent import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.prefabs.configuration.components.ChildOnPrefab diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt index 530a383a4..8b048fd4f 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt @@ -1,7 +1,7 @@ package com.mineinabyss.geary.prefabs.configuration.systems -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.accessors.Pointers import com.mineinabyss.geary.systems.accessors.RelationWithData diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt index 105d5c869..9977c96da 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt @@ -1,8 +1,8 @@ package com.mineinabyss.geary.prefabs.systems +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.datatypes.Records -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.prefabs.PrefabKey import com.mineinabyss.geary.prefabs.prefabs import com.mineinabyss.geary.systems.Listener diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/PrefabTests.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/PrefabTests.kt index eb2bf2248..5be3c5c10 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/PrefabTests.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/PrefabTests.kt @@ -34,7 +34,7 @@ class PrefabTests { } @Test - fun `should track prefabs when key added`(){ + fun `should track prefabs when key added`() { // arrange & act val prefab = entity { set(testKey) } diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/SerializerTest.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/SerializerTest.kt index 911865b18..93bd8a48a 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/SerializerTest.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/SerializerTest.kt @@ -1,7 +1,7 @@ package com.mineinabyss.geary.prefabs -import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.formats.YamlFormat +import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import io.kotest.matchers.shouldBe import kotlinx.serialization.Polymorphic import kotlinx.serialization.PolymorphicSerializer diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index cb5cd0c12..617a30a5e 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -1,19 +1,17 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid -import com.mineinabyss.geary.components.events.EntityRemoved -import com.mineinabyss.geary.datatypes.Records -import com.mineinabyss.geary.datatypes.family.family -import com.mineinabyss.geary.systems.GearyListener -import com.mineinabyss.geary.systems.accessors.Pointers - +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.system import com.mineinabyss.geary.uuid.uuid2Geary -class UnTrackUuidOnRemove : GearyListener() { - private val Pointers.uuid by get().on(target) - private val Pointers.removed by family { has() }.on(event) - - override fun Pointers.handle() { +//TODO fix +fun untrackUuidOnRemove() = geary.system(object : EventQuery() { + val uuid by target.get() +// val removed by event.family { has() } +}) { + onTick { uuid2Geary.remove(uuid) } } diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt index ad8a6c0d8..31ac78934 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt @@ -2,13 +2,11 @@ package com.mineinabyss.geary.benchmarks import com.mineinabyss.geary.benchmarks.helpers.oneMil import com.mineinabyss.geary.benchmarks.helpers.tenMil -import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.RepeatingSystem import com.mineinabyss.geary.systems.query.Query -import com.mineinabyss.geary.systems.query.system +import com.mineinabyss.geary.systems.system import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup @@ -19,7 +17,7 @@ class VelocitySystemBenchmark { data class Velocity(val x: Float, val y: Float) data class Position(var x: Float, var y: Float) - val VelocitySystem = geary.system(object : Query() { + val velocitySystem = geary.system(object : Query() { val velocity by target.get() var position by target.get() }) { @@ -46,7 +44,7 @@ class VelocitySystemBenchmark { @Benchmark fun velocitySystem() { - createVelocitySystem().tickAll() + velocitySystem.tick() } // Theoretical performance with zero ECS overhead diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt index 54e0f8373..81d256d40 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/Family.kt @@ -7,8 +7,7 @@ import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty -sealed interface Family : ReadOnlyAccessor, FamilyMatching { - +sealed interface Family { sealed class Leaf : Family { sealed interface Component : Family { val component: ComponentId @@ -41,10 +40,4 @@ sealed interface Family : ReadOnlyAccessor, FamilyMatching { val or: List } } - - // Helpers for writing queries - override val family: Family? get() = this - override fun getValue(thisRef: Query, property: KProperty<*>): Family { - return this - } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/MutableFamily.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/MutableFamily.kt index 1ff3624d8..ab56f3270 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/MutableFamily.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/family/MutableFamily.kt @@ -9,7 +9,7 @@ import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.modules.geary -inline fun family(init: MutableFamily.Selector.And.() -> Unit): Family { +inline fun family(init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { return MutableFamily.Selector.And().apply(init) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt index 8f5914ea5..3313812b3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt @@ -1,8 +1,9 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.addons.GearyPhase -import com.mineinabyss.geary.systems.RepeatingSystem +import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.System +import com.mineinabyss.geary.systems.TrackedSystem interface Pipeline { fun runOnOrAfter(phase: GearyPhase, block: () -> Unit) @@ -10,10 +11,12 @@ interface Pipeline { fun runStartupTasks() /** Adds a [system] to the engine, which will be ticked appropriately by the engine. */ - fun addSystem(system: System) + fun addSystem(system: System): TrackedSystem fun addSystems(vararg systems: System) + fun addListener(listener: Listener<*>): Listener<*> + /** Gets all registered systems in the order they should be executed during an engine tick. */ - fun getRepeatingInExecutionOrder(): Iterable + fun getRepeatingInExecutionOrder(): Iterable } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt index da7e0bd16..b223a512c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt @@ -3,15 +3,17 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.RepeatingSystem import com.mineinabyss.geary.systems.System +import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import kotlin.time.Duration class PipelineImpl : Pipeline { private val queryManager get() = geary.queryManager private val onSystemRegister = mutableListOf<(System) -> System?>() - private val registeredSystems: MutableSet = mutableSetOf() - private val registeredListeners: MutableSet = mutableSetOf() + private val registeredSystems: MutableSet = mutableSetOf() + private val registeredListeners: MutableSet> = mutableSetOf() private val scheduled = Array(GearyPhase.entries.size) { mutableListOf<() -> Unit>() } private var currentPhase = GearyPhase.entries.first() @@ -31,33 +33,33 @@ class PipelineImpl : Pipeline { } } - override fun addSystem(system: System) { - val resultSystem = onSystemRegister.fold(system) { acc, func -> func(acc) ?: return } + override fun addSystem(system: System): TrackedSystem { +// val resultSystem = onSystemRegister.fold(system) { acc, func -> func(acc) ?: return } // Track systems right at startup since they are likely going to tick very soon anyway, and we don't care about // any hiccups at that point. - when (resultSystem) { - is RepeatingSystem -> { - if (resultSystem in registeredSystems) return - queryManager.trackQuery(resultSystem) - registeredSystems.add(resultSystem) - } - - is Listener -> { - if (resultSystem in registeredListeners) return - resultSystem.start() - queryManager.trackEventListener(resultSystem) - registeredListeners.add(resultSystem) - } - - else -> resultSystem.onStart() + val runner = CachedQueryRunner(system.query) + val tracked = TrackedSystem(system, runner) + if (system.interval != Duration.ZERO) { + registeredSystems.add(tracked) } + return TrackedSystem(system, runner) +// when (resultSystem) { +// else -> resultSystem.onStart() +// } } override fun addSystems(vararg systems: System) { systems.forEach { addSystem(it) } } - override fun getRepeatingInExecutionOrder(): Iterable { + override fun addListener(listener: Listener<*>): Listener<*> { + if (listener in registeredListeners) return listener + queryManager.trackEventListener(listener) + registeredListeners.add(listener) + return listener + } + + override fun getRepeatingInExecutionOrder(): Iterable { return registeredSystems } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt index db033846b..794ce19b6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/QueryManager.kt @@ -8,7 +8,7 @@ import com.mineinabyss.geary.systems.query.GearyQuery import com.mineinabyss.geary.systems.query.Query interface QueryManager { - fun trackEventListener(listener: Listener) + fun trackEventListener(listener: Listener<*>) fun trackQuery(query: T): CachedQueryRunner /** Returns a list of entities matching the given family. */ diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/Archetype.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/Archetype.kt index 4663986e8..768b15141 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/Archetype.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/Archetype.kt @@ -66,11 +66,11 @@ class Archetype internal constructor( /** The amount of entities stored in this archetype. */ val size: Int get() = ids.size - val sourceListeners = mutableListOf() + val sourceListeners = mutableListOf>() - val targetListeners = mutableListOf() + val targetListeners = mutableListOf>() - val eventListeners = mutableListOf() + val eventListeners = mutableListOf>() // ==== Helper functions ==== fun getEntity(row: Int): Entity { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt index 4cb928452..596ba0506 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.engine.archetypes import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.engine.* import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.RepeatingSystem +import com.mineinabyss.geary.systems.TrackedSystem import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.time.Duration @@ -24,8 +24,8 @@ open class ArchetypeEngine(override val tickDuration: Duration) : TickingEngine( (CoroutineScope(Dispatchers.Default) + CoroutineName("Geary Engine")).coroutineContext /** Describes how to individually tick each system */ - protected open fun RepeatingSystem.runSystem() { - tickAll() + protected open fun TrackedSystem.runSystem() { + system.onTick(runner) } override fun scheduleSystemTicking() { @@ -41,7 +41,7 @@ open class ArchetypeEngine(override val tickDuration: Duration) : TickingEngine( override fun tick(currentTick: Long) { // Create a job but don't start it pipeline.getRepeatingInExecutionOrder() - .filter { currentTick % (it.interval / tickDuration).toInt().coerceAtLeast(1) == 0L } + .filter { currentTick % (it.system.interval / tickDuration).toInt().coerceAtLeast(1) == 0L } .also { logger.v("Ticking engine with systems $it") } .forEach { system -> runCatching { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt index 98e157296..b90341ca7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt @@ -2,8 +2,6 @@ package com.mineinabyss.geary.engine.archetypes import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.Record -import com.mineinabyss.geary.datatypes.RecordPointer -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.datatypes.maps.TypeMap import com.mineinabyss.geary.engine.EventRunner import com.mineinabyss.geary.helpers.fastForEach @@ -17,39 +15,41 @@ class ArchetypeEventRunner : EventRunner { callEvent(records[target], records[event], source?.let { records[source] }) } - fun callEvent(target: Record, event: Record, source: Record?) { val eventArc = event.archetype val targetArc = target.archetype val sourceArc = source?.archetype - fun callListener(listener: Listener) { - val pointers: Records = when (source) { - null -> Records(RecordPointer(target), RecordPointer(event), null) - else -> Records(RecordPointer(target), RecordPointer(event), RecordPointer(source)) - } - with(listener) { - pointers.handle() + fun callListener(listener: Listener<*>) { + val query = listener.query + query.event.currArchetype = event.archetype + query.event.currRow = event.row + query.target.currArchetype = target.archetype + query.target.currRow = target.row + if (source != null) { + query.source.currArchetype = source.archetype + query.source.currRow = source.row } + listener.run() } targetArc.targetListeners.fastForEach { - if ((it.event.isEmpty || it in eventArc.eventListeners) && - (it.source.isEmpty || it in (sourceArc?.sourceListeners ?: emptySet())) + if ((it.event.and.isEmpty() || it in eventArc.eventListeners) && + (it.source.and.isEmpty() || it in (sourceArc?.sourceListeners ?: emptySet())) ) callListener(it) } eventArc.eventListeners.fastForEach { // Check empty target to not double call listeners - if (it.target.isEmpty && - (it.event.isEmpty || it in eventArc.eventListeners) && - (it.source.isEmpty || it in (sourceArc?.sourceListeners ?: emptySet())) + if (it.target.and.isEmpty() && + (it.event.and.isEmpty() || it in eventArc.eventListeners) && + (it.source.and.isEmpty() || it in (sourceArc?.sourceListeners ?: emptySet())) ) callListener(it) } sourceArc?.sourceListeners?.fastForEach { // Likewise both target and event must be empty to not double call listeners - if (it.target.isEmpty && it.event.isEmpty && - (it.target.isEmpty || it in targetArc.targetListeners) && - (it.event.isEmpty || it in (eventArc.eventListeners)) + if (it.target.and.isEmpty() && it.event.and.isEmpty() && + (it.target.and.isEmpty() || it in targetArc.targetListeners) && + (it.event.and.isEmpty() || it in (eventArc.eventListeners)) ) callListener(it) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt index 7a60044b8..dd659b860 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt @@ -5,13 +5,13 @@ import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap import com.mineinabyss.geary.engine.QueryManager import com.mineinabyss.geary.helpers.contains -import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.query.CachedQueryRunner -import com.mineinabyss.geary.systems.query.GearyQuery import com.mineinabyss.geary.systems.query.Query import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.synchronized +typealias Listener = com.mineinabyss.geary.systems.Listener<*> + class ArchetypeQueryManager : QueryManager { private val queries = mutableListOf>() private val sourceListeners = mutableListOf() @@ -28,20 +28,20 @@ class ArchetypeQueryManager : QueryManager { val archetypeCount get() = archetypes.elements.size override fun trackEventListener(listener: Listener) { - if (!listener.event.isEmpty) { - val eventFamilyMatch = archetypes.match(listener.event.family) + if (listener.event.and.isNotEmpty()) { + val eventFamilyMatch = archetypes.match(listener.event) for (archetype in eventFamilyMatch) archetype.eventListeners += listener eventListeners.add(listener) } // Only start tracking a listener for the parts it actually cares for - if (!listener.source.isEmpty) { - val sourcesMatched = archetypes.match(listener.source.family) + if (listener.source.and.isNotEmpty()) { + val sourcesMatched = archetypes.match(listener.source) for (archetype in sourcesMatched) archetype.sourceListeners += listener sourceListeners.add(listener) } - if (!listener.target.isEmpty) { - val targetsMatched = archetypes.match(listener.target.family) + if (listener.target.and.isNotEmpty()) { + val targetsMatched = archetypes.match(listener.target) for (archetype in targetsMatched) archetype.targetListeners += listener targetListeners.add(listener) } @@ -81,9 +81,9 @@ class ArchetypeQueryManager : QueryManager { fun getQueriesMatching(archetype: Archetype): MatchedQueries { val matched = queries.filter { archetype.type in it.family } - val matchedSources = sourceListeners.filter { archetype.type in it.source.family } - val matchedTargets = targetListeners.filter { archetype.type in it.target.family } - val matchedEvents = eventListeners.filter { archetype.type in it.event.family } + val matchedSources = sourceListeners.filter { archetype.type in it.source } + val matchedTargets = targetListeners.filter { archetype.type in it.target } + val matchedEvents = eventListeners.filter { archetype.type in it.event } return MatchedQueries(matched, matchedSources, matchedTargets, matchedEvents) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/events/CheckingListener.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/events/CheckingListener.kt index 17407836e..c7f7395fd 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/events/CheckingListener.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/events/CheckingListener.kt @@ -4,23 +4,3 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.RequestCheck import com.mineinabyss.geary.components.events.FailedCheck import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers - -/** - * A listener that runs a check on matched events, adding [FailedCheck] to the event when the check fails. - */ -abstract class CheckingListener() : Listener() { - init { - event.mutableFamily.has() - } - - abstract fun Pointers.check(): Boolean - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - if (!check()) event.entity.apply { - remove() - add() - } - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt index 154960c8c..385d4029d 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt @@ -1,5 +1,4 @@ package com.mineinabyss.geary.systems -typealias GearyListener = Listener +typealias GearyListener = Listener typealias GearySystem = System -typealias GearyRepeatingSystem = RepeatingSystem diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt index 8b6b5bdea..6d7b85c76 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt @@ -1,72 +1,15 @@ package com.mineinabyss.geary.systems -import com.mineinabyss.geary.components.events.ExtendedEntity -import com.mineinabyss.geary.datatypes.GearyEntity -import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.helpers.toGeary -import com.mineinabyss.geary.systems.accessors.* -import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor - -/** - * #### [Guide: Listeners](https://wiki.mineinabyss.com/geary/guide/listeners) - * - * Exposes a way to match against certain combinations of [source]/[target]/[event] entities present on a fired event. - * - * [Handler]s can be defined inside by annotating a function with [Handler], these - * are the actual functions that run when a matching event is found. - */ -abstract class Listener : AccessorOperations(), System { - val target: AccessorHolder = AccessorHolder() - val event: AccessorHolder = AccessorHolder() - val source: AccessorHolder = AccessorHolder() - - fun start() { - onStart() - } - - private fun getIndexForHolder(holder: AccessorHolder): Int = when (holder) { - target -> 0 - event -> 1 - source -> 2 - else -> error("Holder is not a part of this listener: $holder") - } - - fun , A> T.on(holder: AccessorHolder): ReadOnlyEntitySelectingAccessor { - val index = getIndexForHolder(holder) - if (this is FamilyMatching) this.family?.let { holder.mutableFamily.add(it) } - return ReadOnlyEntitySelectingAccessor(this, index) - } - - fun , A> T.on(holder: AccessorHolder): ReadWriteEntitySelectingAccessor { - val index = getIndexForHolder(holder) - if (this is FamilyMatching) this.family?.let { holder.mutableFamily.add(it) } - return ReadWriteEntitySelectingAccessor(this, index) - } - - /** Fires when an entity has a component of type [T] set or updated. */ - inline fun , reified A> T.whenSetOnTarget(): ReadWriteEntitySelectingAccessor { - event.mutableFamily.onSet(componentId()) - return this.on(target) - } - - /** Fires when an entity has a component of type [T] set, only if it was not set before. */ - inline fun , reified A> T.whenFirstSetOnTarget(): ReadWriteEntitySelectingAccessor { - event.mutableFamily.onFirstSet(componentId()) - return this.on(target) - } - - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - inline fun , reified A> T.whenAddedOnTarget(): ReadWriteEntitySelectingAccessor { - event.mutableFamily.onAdd(componentId()) - return this.on(event) - } - - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - fun whenExtendedEntity(): ReadOnlyEntitySelectingAccessor, GearyEntity> { - event.mutableFamily.onExtendedEntity() - return getRelations().map { it.single().target.toGeary() }.on(event) - } - - abstract fun Pointers.handle() +import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.systems.query.EventQuery + +class Listener internal constructor( + val query: T, + val families: EventQuery.Families, + val handle: T.() -> Unit, +) { + fun run() = handle(query) + val event: Family.Selector.And get() = families.event + val source: Family.Selector.And get() = families.source + val target: Family.Selector.And get() = families.target } - diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt new file mode 100644 index 000000000..47b2e822d --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt @@ -0,0 +1,58 @@ +package com.mineinabyss.geary.systems + +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.components.RequestCheck +import com.mineinabyss.geary.components.events.FailedCheck +import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.systems.query.EventQuery + +fun GearyModule.listener( + query: T, + handle: T.() -> Unit +): Listener<*> { +// query.buildFamily() + val listener = Listener( + query, + query.buildFamilies(), + handle, + ) + return pipeline.addListener(listener) +} + +@OptIn(UnsafeAccessors::class) +fun GearyModule.checkingListener( + query: T, + check: T.() -> Boolean +): Listener<*> { + val families = query.buildFamilies() + val listener = Listener( + query, + families.copy(event = family { + has() + add(families.event) + }), + ) { + if (!check()) event.entity.apply { + remove() + add() + } + } + return pipeline.addListener(listener) +} + +/** + * A listener that runs a check on matched events, adding [FailedCheck] to the event when the check fails. + */ +//TODO reimplement +//abstract class CheckingListener() : Listener() { +// init { +// event.mutableFamily.has() +// } +// +// abstract fun Pointers.check(): Boolean +// +// @OptIn(UnsafeAccessors::class) +// override fun Pointers.handle() { +// } +//} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt similarity index 50% rename from geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt rename to geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt index c3fe0db45..67d527a46 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/NewQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt @@ -1,26 +1,19 @@ -package com.mineinabyss.geary.systems.query +package com.mineinabyss.geary.systems -import com.mineinabyss.geary.modules.GearyConfiguration import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.systems.System +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds inline fun GearyModule.system( query: T, init: SystemBuilder.() -> Unit -): System { +): TrackedSystem { val system = SystemBuilder(query).apply(init).build() return pipeline.addSystem(system) } -class SystemTick { - inline fun forEach(run: T.() -> Unit) { - - } -} - class SystemBuilder(val query: T) { @PublishedApi internal var onTick: CachedQueryRunner.() -> Unit = {} @@ -29,31 +22,14 @@ class SystemBuilder(val query: T) { inline fun onTick(crossinline run: T.() -> Unit) { onTick = { forEach(run) } } + fun onTickAll(run: CachedQueryRunner.() -> Unit) { onTick = run } + fun build(): System = System( query, - onTick as CachedQueryRunner<*>.() -> Unit , + onTick as CachedQueryRunner<*>.() -> Unit, interval ) } - -fun printStringSystem() = system(object : Query() { - var string by target.get() -}) { - onTick { - println(string) - } -} - -fun main() { - system(object : Query() { - var string by target.get().removable() - }) { - onTick { - println(string) - } - interval = 2.seconds - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolder.kt deleted file mode 100644 index 32c0e7e50..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolder.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.mineinabyss.geary.systems.accessors - -import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.datatypes.family.MutableFamily - - -/** - * A holder of [IndexedAccessor]s that provides logic for reading data off them and calculating their per-archetype cache. - * - * @property family A lazily built immutable family that represents all data this holder needs to function. - */ -open class AccessorHolder : AccessorOperations() { - val family: Family.Selector.And get() = mutableFamily - - @PublishedApi - internal val mutableFamily: MutableFamily.Selector.And = MutableFamily.Selector.And() - - - /** Is the family of this holder not restricted in any way? */ - val isEmpty: Boolean get() = family.and.isEmpty() -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index d7b924db3..607a3f53d 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -3,6 +3,8 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.HOLDS_DATA import com.mineinabyss.geary.datatypes.Relation +import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.MutableFamily import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable @@ -27,7 +29,7 @@ open class AccessorOperations { * As a result, the type is nullable since it may be removed during system runtime. */ fun ComponentAccessor.removable(): RemovableComponentAccessor { - return RemovableComponentAccessor(id) + return RemovableComponentAccessor(queriedEntity, id) } /** @@ -41,9 +43,10 @@ open class AccessorOperations { /** Maps an accessor, will recalculate on every call. */ fun > A.map(mapping: (T) -> U): ReadOnlyAccessor { return object : ReadOnlyAccessor, FamilyMatching { + override val queriedEntity: QueriedEntity = TODO() override val family = (this@map as? FamilyMatching)?.family - override fun getValue(thisRef: Query, property: KProperty<*>): U { + override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): U { val value = this@map.getValue(thisRef, property) return mapping(value) } @@ -68,11 +71,16 @@ open class AccessorOperations { * - Note: nullability rules are still upheld with [Any]. */ inline fun QueriedEntity.getRelations(): RelationsAccessor { - return RelationsAccessor(componentIdWithNullable(), componentIdWithNullable()) + return RelationsAccessor(this, componentIdWithNullable(), componentIdWithNullable()) } /** @see getRelations */ inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { - return RelationsWithDataAccessor(componentIdWithNullable(), componentIdWithNullable()) + return RelationsWithDataAccessor(this, componentIdWithNullable(), componentIdWithNullable()) + } + + fun QueriedEntity.family(init: MutableFamily.Selector.And.() -> Unit) { + val family = com.mineinabyss.geary.datatypes.family.family(init) + extraFamilies.add(family) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt index de784618c..312afac6e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt @@ -4,5 +4,5 @@ import com.mineinabyss.geary.systems.query.Query import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty -typealias ReadOnlyAccessor = ReadOnlyProperty -typealias ReadWriteAccessor = ReadWriteProperty +typealias ReadOnlyAccessor = ReadOnlyProperty +typealias ReadWriteAccessor = ReadWriteProperty diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt index 0aed8ed13..f7bf16ca4 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt @@ -1,11 +1,13 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.systems.query.QueriedEntity /** * Used for accessors that require a family to be matched against to work * (ex a component accessor needs the component present on the entity.) */ interface FamilyMatching { + val queriedEntity: QueriedEntity val family: Family? } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadOnlyEntitySelectingAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadOnlyEntitySelectingAccessor.kt deleted file mode 100644 index c319354ae..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadOnlyEntitySelectingAccessor.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.mineinabyss.geary.systems.accessors - -import com.mineinabyss.geary.datatypes.Records -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -/** Wrapper for a [ReadOnlyAccessor] that selects a specific entity when multiple can be chosen from. */ -open class ReadOnlyEntitySelectingAccessor, A>( - protected val accessor: T, - protected val pointerIndex: Int, -) : ReadOnlyProperty { - override fun getValue(thisRef: Records, property: KProperty<*>): A { - return accessor.getValue(thisRef.getByIndex(pointerIndex), property) - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadWriteEntitySelectingAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadWriteEntitySelectingAccessor.kt deleted file mode 100644 index 260351d8a..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/ReadWriteEntitySelectingAccessor.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.mineinabyss.geary.systems.accessors - -import com.mineinabyss.geary.datatypes.Records -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -/** Wrapper for a [ReadWriteAccessor] that selects a specific entity when multiple can be chosen from. */ -class ReadWriteEntitySelectingAccessor, A>( - accessor: T, - pointerIndex: Int -) : ReadOnlyEntitySelectingAccessor(accessor, pointerIndex), ReadWriteProperty { - override fun setValue(thisRef: Records, property: KProperty<*>, value: A) { - accessor.setValue(thisRef.getByIndex(pointerIndex), property, value) - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 4f9e0f86a..8ca5d3f82 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -5,16 +5,15 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching -import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.accessors.ReadWriteAccessor import com.mineinabyss.geary.systems.query.QueriedEntity -import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) abstract class ComponentAccessor( - val entity: QueriedEntity, + override val queriedEntity: QueriedEntity, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { override val family: Family = family { hasSet(id) } @@ -23,37 +22,37 @@ abstract class ComponentAccessor( protected var cachedDataArray: MutableList = mutableListOf() protected var cachedArchetype: Archetype? = null - abstract operator fun get(thisRef: Query): T + abstract operator fun get(thisRef: AccessorOperations): T - internal inline fun get(thisRef: Query, beforeRead: () -> Unit): T { - val archetype = entity.currArchetype + internal inline fun get(thisRef: AccessorOperations, beforeRead: () -> Unit): T { + val archetype = queriedEntity.currArchetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeRead() - return cachedDataArray[entity.currEntityIndex] + return cachedDataArray[queriedEntity.currRow] } - abstract operator fun set(thisRef: Query, value: T) + abstract operator fun set(thisRef: AccessorOperations, value: T) - internal inline fun set(thisRef: Query, value: T, beforeWrite: () -> Unit) { - val archetype = entity.currArchetype + internal inline fun set(thisRef: AccessorOperations, value: T, beforeWrite: () -> Unit) { + val archetype = queriedEntity.currArchetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeWrite() - cachedDataArray[entity.currEntityIndex] = value + cachedDataArray[queriedEntity.currRow] = value } - final override fun getValue(thisRef: Query, property: KProperty<*>): T { + final override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): T { return get(thisRef) } - final override fun setValue(thisRef: Query, property: KProperty<*>, value: T) { + final override fun setValue(thisRef: AccessorOperations, property: KProperty<*>, value: T) { return set(thisRef, value) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt index a5f1f24d0..bd6b9483f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @@ -15,7 +16,7 @@ class ComponentOrDefaultAccessor( private var cachedIndex = -1 private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: Query, property: KProperty<*>): T { + override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): T { TODO("Not yet implemented") } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt index b031ffca6..a647bfa97 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt @@ -2,22 +2,21 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.query.QueriedEntity -import com.mineinabyss.geary.systems.query.Query @OptIn(UnsafeAccessors::class) class NonNullComponentAccessor( entity: QueriedEntity, id: ComponentId, ) : ComponentAccessor(entity, id) { - override operator fun get(thisRef: Query): T = + override operator fun get(thisRef: AccessorOperations): T = get(thisRef, beforeRead = {}) - override operator fun set(thisRef: Query, value: T) { + override operator fun set(thisRef: AccessorOperations, value: T) { set(thisRef, value, beforeWrite = { if (cachedIndex == -1) { - entity.entity.set(value, id) + queriedEntity.entity.set(value, id) return } }) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt index 3ece96668..bdf8b2bbe 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt @@ -3,17 +3,17 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import com.mineinabyss.geary.systems.query.QueriedEntity import kotlin.reflect.KProperty -@OptIn(UnsafeAccessors::class) class RelationsAccessor( + override val queriedEntity: QueriedEntity, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>, FamilyMatching { @@ -22,8 +22,8 @@ class RelationsAccessor( private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: Pointer, property: KProperty<*>): List { - val archetype = thisRef.archetype + override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List { + val archetype = queriedEntity.currArchetype if (archetype != cachedArchetype) { cachedArchetype = archetype cachedRelations = archetype.getRelations(kind, target) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt index e352750a8..525434b7b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt @@ -1,20 +1,23 @@ package com.mineinabyss.geary.systems.accessors.type +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.accessors.RelationWithData +import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) class RelationsWithDataAccessor( + override val queriedEntity: QueriedEntity, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>>, FamilyMatching { @@ -23,13 +26,13 @@ class RelationsWithDataAccessor( private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: Pointer, property: KProperty<*>): List> { - val archetype = thisRef.archetype + override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List> { + val archetype = queriedEntity.currArchetype if (archetype != cachedArchetype) { cachedArchetype = archetype cachedRelations = archetype.getRelations(kind, target) } - return archetype.readRelationDataFor(thisRef.row, kind, target, cachedRelations) as List> + return archetype.readRelationDataFor(queriedEntity.currRow, kind, target, cachedRelations) as List> } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt index 0d2ce7d84..16f6b740c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt @@ -2,26 +2,28 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.accessors.AccessorOperations +import com.mineinabyss.geary.systems.query.QueriedEntity -@OptIn(UnsafeAccessors::class) class RemovableComponentAccessor( + entity: QueriedEntity, id: ComponentId, -) : ComponentAccessor(id) { - override fun get(thisRef: Pointer): T? = +) : ComponentAccessor(entity, id) { + override fun get(thisRef: AccessorOperations): T? = get(thisRef, beforeRead = { if (cachedIndex == -1) return null }) - override fun set(thisRef: Pointer, value: T?) = + @OptIn(UnsafeAccessors::class) + override fun set(thisRef: AccessorOperations, value: T?) = set(thisRef, value, beforeWrite = { if (cachedIndex == -1) { if (value == null) return - else thisRef.entity.set(value, id) + else queriedEntity.entity.set(value, id) return } if (value == null) { - thisRef.entity.remove(id) + queriedEntity.entity.remove(id) return } }) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index feb6ef2e7..e15a1ed78 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -5,7 +5,7 @@ import com.mineinabyss.geary.helpers.fastForEachWithIndex class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() - val family = query.buildFamily() + val family = query.target.buildFamily() inline fun toList(crossinline map: T.() -> R): List { val list = mutableListOf() @@ -27,7 +27,7 @@ class CachedQueryRunner internal constructor(val query: T) { // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration target.currArchetype = archetype for (entityIndex in 0 until upTo) { - target.currEntityIndex = entityIndex + target.currRow = entityIndex run(query) } archetype.isIterating = false diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt index 3b2d72c40..8563324cf 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt @@ -1,6 +1,71 @@ package com.mineinabyss.geary.systems.query -abstract class EventQuery() : Query() { +import com.mineinabyss.geary.components.events.ExtendedEntity +import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.systems.accessors.AccessorOperations +import com.mineinabyss.geary.systems.accessors.FamilyMatching +import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor +import kotlin.reflect.KProperty + +abstract class EventQuery : AccessorOperations() { + val target: QueriedEntity = QueriedEntity() val event: QueriedEntity = QueriedEntity() val source: QueriedEntity = QueriedEntity() + + data class Families( + val event: Family.Selector.And, + val target: Family.Selector.And, + val source: Family.Selector.And, + ) + + fun buildFamilies(): Families = Families( + event = event.buildFamily(), + target = target.buildFamily(), + source = source.buildFamily(), + ) + + /** Automatically matches families for any accessor that's supposed to match a family. */ + operator fun T.provideDelegate( + thisRef: Any, + prop: KProperty<*> + ): T { + family?.let { queriedEntity.props[prop] = it } + return this + } + +// /** Fires when an entity has a component of type [T] set or updated. */ +// inline fun , reified A> T.whenSetOnTarget(): ReadWriteEntitySelectingAccessor { +// event.mutableFamily.onSet(componentId()) +// return this.on(target) +// } + + /** Fires when an entity has a component of type [T] set, only if it was not set before. */ + inline fun , reified A> T.whenFirstSetOnTarget(): ReadWriteEntitySelectingAccessor { + event.mutableFamily.onFirstSet(componentId()) + return this.on(target) + } + + /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ + inline fun , reified A> T.whenAddedOnTarget(): ReadWriteEntitySelectingAccessor { + event.mutableFamily.onAdd(componentId()) + return this.on(event) + } + + /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ + fun whenExtendedEntity(): ReadOnlyEntitySelectingAccessor, GearyEntity> { + event.mutableFamily.onExtendedEntity() + return getRelations().map { it.single().target.toGeary() }.on(event) + } + + fun onAnySet(vararg props: KProperty<*>) { + event.family { + props.mapNotNull { event.props[it] }.forEach { + if (it is Family.Leaf.Component) + onSet(it.component) + // TODO do we error here if not, this isn't really typesafe? + } + } + } + } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index 6c2bbc9a7..31093d264 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -1,17 +1,30 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.modules.archetypes +import kotlin.reflect.KProperty class QueriedEntity { + internal val extraFamilies: MutableList = mutableListOf() + + internal val props: MutableMap, Family> = mutableMapOf() + @PublishedApi internal var currArchetype: Archetype = archetypes.archetypeProvider.rootArchetype + @PublishedApi - internal var currEntityIndex: Int = 0 + internal var currRow: Int = 0 @UnsafeAccessors - val entity: GearyEntity = TODO() + val entity: GearyEntity get() = currArchetype.getEntity(currRow) + + fun buildFamily(): Family.Selector.And = family { + for (family in props.values.union(extraFamilies)) { + add(family) + } + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index 373421f20..bd28361b7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -2,30 +2,23 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty abstract class Query( - val baseFamily: Family = family { } + val baseFamily: Family = family { } ) : AccessorOperations() { - val target: QueriedEntity = QueriedEntity() - private val props: MutableMap, Family> = mutableMapOf() - - fun buildFamily(): Family = family { - for (family in props.values) { - add(family) - } - } - + //TODO duplicate with EventQuery /** Automatically matches families for any accessor that's supposed to match a family. */ operator fun T.provideDelegate( thisRef: Any, prop: KProperty<*> ): T { - family?.let { props[prop] = it } + family?.let { queriedEntity.props[prop] = it } return this } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt index 9df23b19d..5e0d11afa 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt @@ -1,31 +1,25 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointers -import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.checkingListener +import com.mineinabyss.geary.systems.query.EventQuery import io.kotest.matchers.shouldBe import kotlin.test.Test class CheckingListenerTest : GearyTest() { class MyEvent() - class MyListener : CheckingListener() { - var called = 0 - - val Records.data by get().on(target) - - override fun Pointers.check(): Boolean { - return data > 10 - } + fun myListener() = geary.checkingListener(object: EventQuery() { + val data by target.get() + }) { + data > 10 } @Test fun `simple set listener`() { - val listener = MyListener() - geary.pipeline.addSystem(listener) + myListener() val entityFail = entity { set(1) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index 587a8dad8..f94478c1c 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -1,34 +1,34 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.datatypes.Records +import com.mineinabyss.geary.components.events.UpdatedComponent import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.EventQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test internal class ComponentAddEventTest : GearyTest() { var inc = 0 - //TODO write test for all methods of checking for added - inner class OnStringAdd : Listener() { - // All three get added - val Records.string by get().whenSetOnTarget() - val Records.int by get().whenSetOnTarget() - val Records.double by get().whenSetOnTarget() + fun onStringAdd() = geary.listener(object : EventQuery() { + val string by target.get() + val int by target.get() + val double by target.get() - override fun Pointers.handle() { - inc++ + init { + event.family { + hasRelation() + } + onAnySet(::string, ::int, ::double) } - } + }) { inc++ } @Test fun componentAddEvent() { - val listener = listener(StringAddQuery()).handle { inc++ } - geary.pipeline.addSystem(listener) + val listener = onStringAdd() entity { fun addedListeners() = type.getArchetype().targetListeners.count { it === listener } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt index c2b3465d6..a314e354d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt @@ -1,43 +1,36 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.EventQuery import io.kotest.matchers.shouldBe import kotlin.test.Test class SimpleEventTest : GearyTest() { class MyEvent() - class MyListener : Listener() { - var called = 0 + var called = 0 - val Records.data by get().on(target) - val Records.event by get().on(event) - - override fun Pointers.handle() { - called += 1 - } + fun myListener() = geary.listener(object : EventQuery() { + val data by target.get() + val myEvent by event.get() + }) { + called++ } - class SourceOnlyListener: Listener() { - var called = 0 - val Records.event by get().on(event) - val Records.data by get().on(source) - - override fun Pointers.handle() { - called += 1 - } + fun sourceOnlyListener() = geary.listener(object : EventQuery() { + val data by source.get() + val myEvent by event.get() + }) { + called++ } @Test fun `simple set listener`() { - val listener = MyListener() - geary.pipeline.addSystem(listener) - + called = 0 + val listener = myListener() val entity = entity { set(1) } @@ -45,19 +38,19 @@ class SimpleEventTest : GearyTest() { set(MyEvent()) } - listener.called shouldBe 0 + called shouldBe 0 entity.callEvent(event) - listener.called shouldBe 1 + called shouldBe 1 entity.callEvent(entity()) - listener.called shouldBe 1 + called shouldBe 1 entity().callEvent(event) - listener.called shouldBe 1 + called shouldBe 1 } @Test fun `source only simple set listener`() { - val listener = SourceOnlyListener() - geary.pipeline.addSystem(listener) + called = 0 + val listener = sourceOnlyListener() val target = entity() val source = entity { @@ -67,8 +60,8 @@ class SimpleEventTest : GearyTest() { set(MyEvent()) } - listener.called shouldBe 0 + called shouldBe 0 target.callEvent(event, source = source) - listener.called shouldBe 1 + called shouldBe 1 } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt index 44a21711d..083c5b844 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt @@ -2,11 +2,8 @@ package com.mineinabyss.geary.queries import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.dsl.track import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer import com.mineinabyss.geary.systems.query.Query -import com.mineinabyss.geary.systems.query.system import io.kotest.matchers.shouldBe import kotlin.test.Test From b19f506d48c7bce4ffc1d72225b5273bbc9536c2 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 22 Feb 2024 23:58:52 +0100 Subject: [PATCH 04/14] Fixing tests for new syntax --- .../com/mineinabyss/geary/prefabs/Prefabs.kt | 4 +- .../systems/ParseChildOnPrefab.kt | 49 ++++++------ .../mineinabyss/geary/uuid/UUIDTracking.kt | 11 +-- .../geary/uuid/systems/TrackUuidOnAdd.kt | 35 ++++---- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 14 ++-- .../geary/datatypes/RecordPointer.kt | 12 +++ .../mineinabyss/geary/engine/PipelineImpl.kt | 2 +- .../engine/archetypes/ArchetypeEventRunner.kt | 16 ++-- .../geary/systems/CachedQueryBuilder.kt | 12 +++ .../com/mineinabyss/geary/systems/Listener.kt | 6 +- .../geary/systems/ListenerBuilder.kt | 79 ++++++++----------- .../systems/accessors/AccessorOperations.kt | 4 +- .../geary/systems/accessors/FamilyMatching.kt | 2 +- .../accessors/type/ComponentAccessor.kt | 10 +-- .../accessors/type/RelationsAccessor.kt | 6 +- .../type/RelationsWithDataAccessor.kt | 6 +- .../type/RemovableComponentAccessor.kt | 7 +- .../geary/systems/query/CachedQueryRunner.kt | 13 ++- .../query/{EventQuery.kt => ListenerQuery.kt} | 47 +++++------ .../geary/systems/query/QueriedEntity.kt | 45 ++++++++--- .../mineinabyss/geary/systems/query/Query.kt | 7 +- .../geary/events/CheckingListenerTest.kt | 9 +-- .../geary/events/ComponentAddEventTest.kt | 16 ++-- .../events/SimpleComponentAddListenerTest.kt | 27 +++---- .../geary/events/SimpleEventTest.kt | 6 +- .../geary/events/SourceTargetEventTest.kt | 24 ++---- .../geary/systems/FamilyMatchingTest.kt | 33 +++----- .../geary/systems/QueryManagerTest.kt | 21 ++--- .../systems/RelationMatchingSystemTest.kt | 58 +++++++------- .../systems/accessors/AccessorHolderTest.kt | 9 ++- .../accessors/RemovableAccessorTest.kt | 25 +++--- .../accessors/UnsafeQueryAccessTests.kt | 56 ++++++------- 32 files changed, 330 insertions(+), 341 deletions(-) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt rename geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/{EventQuery.kt => ListenerQuery.kt} (50%) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt index 2e1c0c5ae..47a4264d1 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt @@ -23,9 +23,9 @@ interface Prefabs { override fun Prefabs.install() { + createParseChildOnPrefabListener() + createParseChildrenOnPrefabListener() geary.pipeline.addSystems( - ParseChildOnPrefab(), - ParseChildrenOnPrefab(), ParseRelationOnPrefab(), ParseRelationWithDataSystem(), TrackPrefabsByKeySystem(), diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt index 9b62221b1..5ba8fe7cd 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt @@ -5,40 +5,37 @@ import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.helpers.addParent import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.ChildOnPrefab import com.mineinabyss.geary.prefabs.configuration.components.ChildrenOnPrefab import com.mineinabyss.geary.prefabs.configuration.components.Prefab -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery -class ParseChildOnPrefab : Listener() { - private var Pointers.child by get().removable().whenSetOnTarget() - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - entity { - addParent(target.entity) - setAll(child!!.components) - } - child = null +@OptIn(UnsafeAccessors::class) +fun createParseChildOnPrefabListener() = geary.listener(object : ListenerQuery() { + var child by target.get().removable() +}.apply { onSet(::child) }).exec { + entity { + addParent(target.entity) + setAll(child!!.components) } + child = null } -class ParseChildrenOnPrefab : Listener() { - private var Pointers.children by get().removable().whenSetOnTarget() - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - children!!.nameToComponents.forEach { (name, components) -> - entity { - set(EntityName(name)) - set(Prefab()) - addParent(target.entity) - addRelation() - setAll(components) - } +@OptIn(UnsafeAccessors::class) +fun createParseChildrenOnPrefabListener() = geary.listener(object : ListenerQuery() { + var children by target.get().removable() +}.apply { onSet(::children) }).exec { + children!!.nameToComponents.forEach { (name, components) -> + entity { + set(EntityName(name)) + set(Prefab()) + addParent(target.entity) + addRelation() + setAll(components) } - children = null } + children = null } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt index d3c15a3ee..39d4a2d25 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt @@ -1,9 +1,8 @@ package com.mineinabyss.geary.uuid import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.uuid.systems.TrackUuidOnAdd -import com.mineinabyss.geary.uuid.systems.UnTrackUuidOnRemove +import com.mineinabyss.geary.uuid.systems.createTrackUUIDOnAddSystem +import com.mineinabyss.geary.uuid.systems.createUntrackUuidOnRemoveSystem import com.mineinabyss.idofront.di.DI val uuid2Geary by DI.observe() @@ -12,9 +11,7 @@ object UUIDTracking : GearyAddonWithDefault { override fun default() = SimpleUUID2GearyMap() override fun UUID2GearyMap.install() { - geary.pipeline.addSystems( - TrackUuidOnAdd(), - UnTrackUuidOnRemove() - ) + createTrackUUIDOnAddSystem() + createUntrackUuidOnRemoveSystem() } } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt index b16e6d672..7d867b4f0 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt @@ -3,26 +3,23 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid import com.benasher44.uuid.uuid4 import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.systems.GearyListener -import com.mineinabyss.geary.systems.accessors.Pointers - +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash import com.mineinabyss.geary.uuid.uuid2Geary -class TrackUuidOnAdd : GearyListener() { - var Pointers.uuid by get().whenSetOnTarget() - val Pointers.regenerateUUIDOnClash by get().orNull().on(target) - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - if (uuid in uuid2Geary) - if (regenerateUUIDOnClash != null) { - val newUuid = uuid4() - uuid = newUuid - uuid2Geary[newUuid] = target.entity - } else error("Tried tracking entity $target.entity with already existing uuid $uuid") - else - uuid2Geary[uuid] = target.entity - } +@OptIn(UnsafeAccessors::class) +fun createTrackUUIDOnAddSystem() = geary.listener(object : ListenerQuery() { + var uuid by target.get() + val regenerateUUIDOnClash by target.get().orNull() +}.apply { onSet(::uuid) }).exec { + if (uuid in uuid2Geary) + if (regenerateUUIDOnClash != null) { + val newUuid = uuid4() + uuid = newUuid + uuid2Geary[newUuid] = target.entity + } else error("Tried tracking entity $target.entity with already existing uuid $uuid") + else + uuid2Geary[uuid] = target.entity } - diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index 617a30a5e..8c222899f 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -1,17 +1,15 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid +import com.mineinabyss.geary.components.events.EntityRemoved import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.query.EventQuery -import com.mineinabyss.geary.systems.system +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.uuid2Geary //TODO fix -fun untrackUuidOnRemove() = geary.system(object : EventQuery() { +fun createUntrackUuidOnRemoveSystem() = geary.listener(object : ListenerQuery() { val uuid by target.get() -// val removed by event.family { has() } -}) { - onTick { - uuid2Geary.remove(uuid) - } +}.apply { event.match { has() } }) { + uuid2Geary.remove(uuid) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt new file mode 100644 index 000000000..64928efb6 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/RecordPointer.kt @@ -0,0 +1,12 @@ +package com.mineinabyss.geary.datatypes + +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.archetypes + +/** A record created in place that delegates to the real entity pointer the first time [entity] gets accessed. */ +class RecordPointer @PublishedApi internal constructor( + archetype: Archetype, + row: Int +) { +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt index b223a512c..c01bd87b7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt @@ -37,7 +37,7 @@ class PipelineImpl : Pipeline { // val resultSystem = onSystemRegister.fold(system) { acc, func -> func(acc) ?: return } // Track systems right at startup since they are likely going to tick very soon anyway, and we don't care about // any hiccups at that point. - val runner = CachedQueryRunner(system.query) + val runner = queryManager.trackQuery(system.query) val tracked = TrackedSystem(system, runner) if (system.interval != Duration.ZERO) { registeredSystems.add(tracked) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt index b90341ca7..e9cfa8df0 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt @@ -22,13 +22,17 @@ class ArchetypeEventRunner : EventRunner { fun callListener(listener: Listener<*>) { val query = listener.query - query.event.currArchetype = event.archetype - query.event.currRow = event.row - query.target.currArchetype = target.archetype - query.target.currRow = target.row + //TODO function extract + query.event.delegated = false + query.event.originalArchetype = event.archetype + query.event.originalRow = event.row + query.target.delegated = false + query.target.originalArchetype = target.archetype + query.target.originalRow = target.row if (source != null) { - query.source.currArchetype = source.archetype - query.source.currRow = source.row + query.source.delegated = false + query.source.originalArchetype = source.archetype + query.source.originalRow = source.row } listener.run() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt new file mode 100644 index 000000000..1b83d4cee --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt @@ -0,0 +1,12 @@ +package com.mineinabyss.geary.systems + +import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query + + +fun GearyModule.cachedQuery( + query: T, +): CachedQueryRunner { + return queryManager.trackQuery(query) +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt index 6d7b85c76..5cd93e31a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/Listener.kt @@ -1,11 +1,11 @@ package com.mineinabyss.geary.systems import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.query.ListenerQuery -class Listener internal constructor( +class Listener internal constructor( val query: T, - val families: EventQuery.Families, + val families: ListenerQuery.Families, val handle: T.() -> Unit, ) { fun run() = handle(query) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt index 47b2e822d..cdebf3c81 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt @@ -4,55 +4,44 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.RequestCheck import com.mineinabyss.geary.components.events.FailedCheck import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.engine.Pipeline import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.query.ListenerQuery -fun GearyModule.listener( - query: T, - handle: T.() -> Unit -): Listener<*> { -// query.buildFamily() - val listener = Listener( - query, - query.buildFamilies(), - handle, - ) - return pipeline.addListener(listener) +fun GearyModule.listener( + query: T +): ListenerBuilder { + return ListenerBuilder(query, pipeline) } -@OptIn(UnsafeAccessors::class) -fun GearyModule.checkingListener( - query: T, - check: T.() -> Boolean -): Listener<*> { - val families = query.buildFamilies() - val listener = Listener( - query, - families.copy(event = family { - has() - add(families.event) - }), - ) { - if (!check()) event.entity.apply { - remove() - add() +class ListenerBuilder( + val query: T, + val pipeline: Pipeline, +) { + fun exec(handle: T.() -> Unit): Listener<*> { + val listener = Listener( + query, + query.buildFamilies(), + handle + ) + return pipeline.addListener(listener) + } + + @OptIn(UnsafeAccessors::class) + fun check(check: T.() -> Boolean): Listener<*> { + val families = query.buildFamilies() + val listener = Listener( + query, + families.copy(event = family { + has() + add(families.event) + }), + ) { + if (!check()) event.entity.apply { + remove() + add() + } } + return pipeline.addListener(listener) } - return pipeline.addListener(listener) } - -/** - * A listener that runs a check on matched events, adding [FailedCheck] to the event when the check fails. - */ -//TODO reimplement -//abstract class CheckingListener() : Listener() { -// init { -// event.mutableFamily.has() -// } -// -// abstract fun Pointers.check(): Boolean -// -// @OptIn(UnsafeAccessors::class) -// override fun Pointers.handle() { -// } -//} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 607a3f53d..be2b5dbba 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -3,14 +3,12 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.HOLDS_DATA import com.mineinabyss.geary.datatypes.Relation -import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.MutableFamily import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.QueriedEntity -import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty open class AccessorOperations { @@ -79,7 +77,7 @@ open class AccessorOperations { return RelationsWithDataAccessor(this, componentIdWithNullable(), componentIdWithNullable()) } - fun QueriedEntity.family(init: MutableFamily.Selector.And.() -> Unit) { + fun QueriedEntity.match(init: MutableFamily.Selector.And.() -> Unit) { val family = com.mineinabyss.geary.datatypes.family.family(init) extraFamilies.add(family) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt index f7bf16ca4..f6dee8d3a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt @@ -9,5 +9,5 @@ import com.mineinabyss.geary.systems.query.QueriedEntity */ interface FamilyMatching { val queriedEntity: QueriedEntity - val family: Family? + val family: Family.Selector? } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 8ca5d3f82..395250c5a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -16,7 +16,7 @@ abstract class ComponentAccessor( override val queriedEntity: QueriedEntity, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { - override val family: Family = family { hasSet(id) } + override val family = family { hasSet(id) } protected var cachedIndex = -1 protected var cachedDataArray: MutableList = mutableListOf() @@ -25,27 +25,27 @@ abstract class ComponentAccessor( abstract operator fun get(thisRef: AccessorOperations): T internal inline fun get(thisRef: AccessorOperations, beforeRead: () -> Unit): T { - val archetype = queriedEntity.currArchetype + val archetype = queriedEntity.archetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeRead() - return cachedDataArray[queriedEntity.currRow] + return cachedDataArray[queriedEntity.row] } abstract operator fun set(thisRef: AccessorOperations, value: T) internal inline fun set(thisRef: AccessorOperations, value: T, beforeWrite: () -> Unit) { - val archetype = queriedEntity.currArchetype + val archetype = queriedEntity.archetype if (archetype !== cachedArchetype) { cachedArchetype = archetype cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } beforeWrite() - cachedDataArray[queriedEntity.currRow] = value + cachedDataArray[queriedEntity.row] = value } final override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): T { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt index bdf8b2bbe..4b5ea91af 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt @@ -1,5 +1,6 @@ package com.mineinabyss.geary.systems.accessors.type +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation @@ -17,13 +18,14 @@ class RelationsAccessor( val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>, FamilyMatching { - override val family: Family = family { hasRelation(kind, target) } + override val family = family { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null + @OptIn(UnsafeAccessors::class) override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List { - val archetype = queriedEntity.currArchetype + val archetype = queriedEntity.archetype if (archetype != cachedArchetype) { cachedArchetype = archetype cachedRelations = archetype.getRelations(kind, target) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt index 525434b7b..bf203658e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt @@ -21,18 +21,18 @@ class RelationsWithDataAccessor( val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>>, FamilyMatching { - override val family: Family = family { hasRelation(kind, target) } + override val family = family { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List> { - val archetype = queriedEntity.currArchetype + val archetype = queriedEntity.archetype if (archetype != cachedArchetype) { cachedArchetype = archetype cachedRelations = archetype.getRelations(kind, target) } - return archetype.readRelationDataFor(queriedEntity.currRow, kind, target, cachedRelations) as List> + return archetype.readRelationDataFor(queriedEntity.row, kind, target, cachedRelations) as List> } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt index 16f6b740c..ac9598d01 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.query.QueriedEntity @@ -19,11 +20,15 @@ class RemovableComponentAccessor( set(thisRef, value, beforeWrite = { if (cachedIndex == -1) { if (value == null) return - else queriedEntity.entity.set(value, id) + else { + queriedEntity.entity.set(value, id) + queriedEntity.delegateTo(archetypes.records[queriedEntity.entity]) + } return } if (value == null) { queriedEntity.entity.remove(id) + queriedEntity.delegateTo(archetypes.records[queriedEntity.entity]) return } }) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index e15a1ed78..560517dd4 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -1,5 +1,6 @@ package com.mineinabyss.geary.systems.query +import com.mineinabyss.geary.datatypes.RecordPointer import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.helpers.fastForEachWithIndex @@ -20,17 +21,23 @@ class CachedQueryRunner internal constructor(val query: T) { * */ inline fun forEach(crossinline run: T.() -> Unit) { val matched = matchedArchetypes - matched.fastForEachWithIndex { i, archetype -> + var n = 0 + val size = matched.size // Get size ahead of time to avoid rerunning on entities that end up in new archetypes + while (n < size) { + val archetype = matched[n] archetype.isIterating = true val upTo = archetype.size val target = query.target // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration - target.currArchetype = archetype + target.originalArchetype = archetype + for (entityIndex in 0 until upTo) { - target.currRow = entityIndex + target.delegated = false + target.originalRow = entityIndex run(query) } archetype.isIterating = false + n++ } } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt similarity index 50% rename from geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt rename to geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index 8563324cf..8c58686b5 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/EventQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -1,14 +1,11 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.components.events.ExtendedEntity import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching -import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor import kotlin.reflect.KProperty -abstract class EventQuery : AccessorOperations() { +abstract class ListenerQuery : AccessorOperations() { val target: QueriedEntity = QueriedEntity() val event: QueriedEntity = QueriedEntity() val source: QueriedEntity = QueriedEntity() @@ -34,38 +31,34 @@ abstract class EventQuery : AccessorOperations() { return this } -// /** Fires when an entity has a component of type [T] set or updated. */ -// inline fun , reified A> T.whenSetOnTarget(): ReadWriteEntitySelectingAccessor { -// event.mutableFamily.onSet(componentId()) -// return this.on(target) -// } - - /** Fires when an entity has a component of type [T] set, only if it was not set before. */ - inline fun , reified A> T.whenFirstSetOnTarget(): ReadWriteEntitySelectingAccessor { - event.mutableFamily.onFirstSet(componentId()) - return this.on(target) - } - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - inline fun , reified A> T.whenAddedOnTarget(): ReadWriteEntitySelectingAccessor { - event.mutableFamily.onAdd(componentId()) - return this.on(event) + fun onExtend() { + event.match { onExtendedEntity() } +// return getRelations().map { it.single().target.toGeary() }.on(event) } /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - fun whenExtendedEntity(): ReadOnlyEntitySelectingAccessor, GearyEntity> { - event.mutableFamily.onExtendedEntity() - return getRelations().map { it.single().target.toGeary() }.on(event) + fun onAdd(vararg props: KProperty<*>) { + } - fun onAnySet(vararg props: KProperty<*>) { - event.family { - props.mapNotNull { event.props[it] }.forEach { - if (it is Family.Leaf.Component) - onSet(it.component) + /** Fires when an entity has a component of type [T] set or updated. */ + fun onSet(vararg props: KProperty<*>) { + val names = props.map { it.name }.toSet() + event.match { + target.props.filterKeys { prop -> + prop.name in names + }.values.flatMap { + it.components + }.forEach { component -> + onSet(component) // TODO do we error here if not, this isn't really typesafe? } } } + /** Fires when an entity has a component of type [T] set, only if it was not set before. */ + fun onFirstSet(vararg props: KProperty<*>) { + + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index 31093d264..f8caf1cc0 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -1,7 +1,8 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.Record import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype @@ -10,21 +11,47 @@ import kotlin.reflect.KProperty class QueriedEntity { internal val extraFamilies: MutableList = mutableListOf() + internal val props: MutableMap, Family.Selector> = mutableMapOf() - internal val props: MutableMap, Family> = mutableMapOf() + fun buildFamily(): Family.Selector.And = family { + for (family in props.values.union(extraFamilies)) { + add(family) + } + } @PublishedApi - internal var currArchetype: Archetype = archetypes.archetypeProvider.rootArchetype + internal var originalArchetype = archetypes.archetypeProvider.rootArchetype @PublishedApi - internal var currRow: Int = 0 + internal var originalRow = 0 @UnsafeAccessors - val entity: GearyEntity get() = currArchetype.getEntity(currRow) + val archetype: Archetype get() = if (delegated) delegate!!.archetype else originalArchetype + val row: Int get() = if (delegated) delegate!!.row else originalRow - fun buildFamily(): Family.Selector.And = family { - for (family in props.values.union(extraFamilies)) { - add(family) - } + private var delegate: Record? = null + + @PublishedApi + internal var delegated = false + + internal fun delegateTo(record: Record) { + delegated = true + delegate = record } + + @UnsafeAccessors + val entity: Entity + get() { + val entity = archetype.getEntity(row) + if (!delegated) { + delegate = archetypes.records[entity] + } + delegated = true + return entity + } + + @UnsafeAccessors + operator fun component1(): Archetype = archetype + + operator fun component2(): Int = row } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index bd28361b7..2b91bcb64 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -1,15 +1,10 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.datatypes.family.family -import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty -abstract class Query( - val baseFamily: Family = family { } -) : AccessorOperations() { +abstract class Query : AccessorOperations() { val target: QueriedEntity = QueriedEntity() //TODO duplicate with EventQuery diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt index 5e0d11afa..9ef639bab 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt @@ -4,18 +4,17 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.checkingListener -import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test class CheckingListenerTest : GearyTest() { class MyEvent() - fun myListener() = geary.checkingListener(object: EventQuery() { + fun myListener() = geary.listener(object: ListenerQuery() { val data by target.get() - }) { - data > 10 - } + }).check { data > 10 } @Test fun `simple set listener`() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index f94478c1c..15d28bdcb 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -1,30 +1,24 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.components.events.UpdatedComponent import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.listener -import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test internal class ComponentAddEventTest : GearyTest() { var inc = 0 - fun onStringAdd() = geary.listener(object : EventQuery() { + fun onStringAdd() = geary.listener(object : ListenerQuery() { val string by target.get() val int by target.get() val double by target.get() - - init { - event.family { - hasRelation() - } - onAnySet(::string, ::int, ::double) - } - }) { inc++ } + }.apply { onSet(::string, ::int, ::double) }) { + inc++ + } @Test fun componentAddEvent() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt index 8db93fbfb..4dcb60573 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt @@ -1,35 +1,28 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test class SimpleComponentAddListenerTest : GearyTest() { - class MyListener : Listener() { - var called = 0 - - val Records.data by get().whenSetOnTarget() - - override fun Pointers.handle() { - called += 1 - } - } + var called = 0 + fun myListener() = geary.listener(object : ListenerQuery() { + val data by target.get() + }.apply { onSet(::data) }) { called += 1 } @Test fun `simple event listener`() { - val listener = MyListener() - geary.pipeline.addSystem(listener) + myListener() val entity = entity() - listener.called shouldBe 0 + called shouldBe 0 entity.set(1.0) - listener.called shouldBe 0 + called shouldBe 0 entity.set(1) - listener.called shouldBe 1 + called shouldBe 1 } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt index a314e354d..292a17474 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt @@ -4,7 +4,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.listener -import com.mineinabyss.geary.systems.query.EventQuery +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test @@ -13,14 +13,14 @@ class SimpleEventTest : GearyTest() { var called = 0 - fun myListener() = geary.listener(object : EventQuery() { + fun myListener() = geary.listener(object : ListenerQuery() { val data by target.get() val myEvent by event.get() }) { called++ } - fun sourceOnlyListener() = geary.listener(object : EventQuery() { + fun sourceOnlyListener() = geary.listener(object : ListenerQuery() { val data by source.get() val myEvent by event.get() }) { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt index 2acae2260..b89199ec9 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt @@ -1,12 +1,10 @@ package com.mineinabyss.geary.events -import com.mineinabyss.geary.datatypes.Records -import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test @@ -15,22 +13,16 @@ class SourceTargetEventTest : GearyTest() { class Attack data class Health(val amount: Int) - inner class Interaction : Listener() { - val Records.strength by get().on(source) - var Records.health by get().on(target) - - init { - event.mutableFamily.add(family { has() }) - } - - override fun Pointers.handle() { - health = Health(health.amount - strength.amount) - } + fun interactionListener() = geary.listener(object : ListenerQuery() { + val strength by source.get() + var health by target.get() + }.apply { event.match { has() } }) { + health = Health(health.amount - strength.amount) } @Test fun interactions() { - geary.pipeline.addSystem(Interaction()) + interactionListener() val source = entity { set(Strength(10)) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index 833a84865..2013bb19e 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -1,13 +1,13 @@ package com.mineinabyss.geary.systems -import com.mineinabyss.geary.datatypes.HOLDS_DATA import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.HOLDS_DATA import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.shouldBe @@ -17,28 +17,19 @@ class FamilyMatchingTest : GearyTest() { val stringId = componentId() or HOLDS_DATA val intId = componentId() - val system = object : RepeatingSystem() { - val Pointer.string by get() - - // val GearyRecord.int by family { has() } - init { - mutableFamily.has() - } - - @OptIn(UnsafeAccessors::class) - override fun Pointer.tick() { - string shouldBe entity.get() - entity.has() shouldBe true + @OptIn(UnsafeAccessors::class) + val system = geary.system(object : Query() { + val string by target.get() + }.apply { target.match { has() } }) { + onTick { + string shouldBe target.entity.get() + target.entity.has() shouldBe true } } val root = archetypes.archetypeProvider.rootArchetype val correctArchetype = root + stringId + intId - init { - geary.queryManager.trackQuery(system) - } - @Test fun `family type is correct`() { // TODO families can are wrapped by accessors now, so components won't be directly on it @@ -47,7 +38,7 @@ class FamilyMatchingTest : GearyTest() { @Test fun `archetypes have been matched correctly`() { - system.matchedArchetypes shouldContain correctArchetype + system.runner.matchedArchetypes shouldContain correctArchetype } @Test @@ -60,11 +51,11 @@ class FamilyMatchingTest : GearyTest() { set("Test") set(1) } - geary.queryManager.getEntitiesMatching(system.family).shouldContainAll(entity, entity2) + geary.queryManager.getEntitiesMatching(system.runner.family).shouldContainAll(entity, entity2) } @Test fun `accessors in system correctly read data`() { - system.tickAll() + system.tick() } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt index 5b13730fb..f494d79ea 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt @@ -1,35 +1,30 @@ package com.mineinabyss.geary.systems -import com.mineinabyss.geary.datatypes.Records import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test internal class QueryManagerTest : GearyTest() { private class TestComponent - private class EventListener : Listener() { - var ran = 0 - private val Records.testComponent by get().on(target) - override fun Pointers.handle() { - ran++ - } - } + var ran = 0 + fun myListener() = geary.listener(object : ListenerQuery() { + val testComponent by target.get() + }) { ran++ } @Test fun `empty event handler`() { - val listener = EventListener() - geary.pipeline.addSystem(listener) - (archetypes.archetypeProvider.rootArchetype.type in listener.event.family) shouldBe true + val listener = myListener() + (archetypes.archetypeProvider.rootArchetype.type in listener.families.event) shouldBe true entity { set(TestComponent()) }.callEvent() - listener.ran shouldBe 1 + ran shouldBe 1 } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt index 907131a1a..09cbf0eb3 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt @@ -2,13 +2,12 @@ package com.mineinabyss.geary.systems import com.mineinabyss.geary.components.relations.InstanceOf import com.mineinabyss.geary.components.relations.Persists -import com.mineinabyss.geary.datatypes.RecordPointer import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.query.Query import io.kotest.inspectors.forAll import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotContain @@ -21,18 +20,16 @@ import kotlin.test.Test class RelationMatchingSystemTest : GearyTest() { @Test fun relations() { + var ran = 0 resetEngine() - val system = object: RepeatingSystem() { - val Pointer.persists by getRelationsWithData() - - var ran = 0 - - override fun Pointer.tick() { + val system = geary.system(object : Query() { + val persists by target.getRelationsWithData() + }) { + onTick { ran++ persists.forAll { it.data.shouldBeInstanceOf() } } } - geary.pipeline.addSystem(system) val entity = entity { addRelation() @@ -46,12 +43,12 @@ class RelationMatchingSystemTest : GearyTest() { setRelation(Persists()) add() } - (entity3.type in system.family) shouldBe true - system.matchedArchetypes.shouldNotContainAll(entity.type.getArchetype(), entity2.type.getArchetype()) - system.matchedArchetypes.shouldContain(entity3.type.getArchetype()) + (entity3.type in system.runner.family) shouldBe true + system.runner.matchedArchetypes.shouldNotContainAll(entity.type.getArchetype(), entity2.type.getArchetype()) + system.runner.matchedArchetypes.shouldContain(entity3.type.getArchetype()) - geary.engine.tick(0) - system.ran shouldBe 1 + system.tick() + ran shouldBe 1 } @Test @@ -60,11 +57,11 @@ class RelationMatchingSystemTest : GearyTest() { var ran = 0 var persistsCount = 0 var instanceOfCount = 0 - val system = object : RepeatingSystem() { - val RecordPointer.persists by getRelationsWithData() - val RecordPointer.instanceOf by getRelationsWithData() - - override fun RecordPointer.tick() { + val system = geary.system(object : Query() { + val persists by target.getRelationsWithData() + val instanceOf by target.getRelationsWithData() + }) { + onTick { ran++ persistsCount += persists.size instanceOfCount += instanceOf.size @@ -73,7 +70,6 @@ class RelationMatchingSystemTest : GearyTest() { instanceOf.forAll { it.data shouldBe null } } } - geary.pipeline.addSystem(system) entity { setRelation(Persists()) // Yes @@ -93,7 +89,7 @@ class RelationMatchingSystemTest : GearyTest() { addRelation() // No } - geary.engine.tick(0) + system.tick() // Only two of the Persists relations are valid, times both InstanceOf are valid ran shouldBe 1 @@ -104,14 +100,7 @@ class RelationMatchingSystemTest : GearyTest() { @Test fun relationsWithData() { resetEngine() - val system = object : RepeatingSystem() { - val RecordPointer.withData by getRelationsWithData() - override fun RecordPointer.tick() { - withData.forAll { it.data shouldBe Persists() } - withData.forAll { it.targetData shouldBe "Test" } - } - } val entity = entity { setRelation(Persists()) @@ -123,10 +112,17 @@ class RelationMatchingSystemTest : GearyTest() { set("Test") } - geary.queryManager.trackQuery(system) + val system = geary.system(object : Query() { + val withData by target.getRelationsWithData() + }) { + onTick { + withData.forAll { it.data shouldBe Persists() } + withData.forAll { it.targetData shouldBe "Test" } + } + } - system.matchedArchetypes.shouldNotContain(entity.type.getArchetype()) - system.matchedArchetypes.shouldContain(entityWithData.type.getArchetype()) + system.runner.matchedArchetypes.shouldNotContain(entity.type.getArchetype()) + system.runner.matchedArchetypes.shouldContain(entityWithData.type.getArchetype()) geary.engine.tick(0) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt index 17d0e7632..0ddd6d48b 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt @@ -2,14 +2,14 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.systems.query.GearyQuery +import com.mineinabyss.geary.systems.query.Query import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.jupiter.api.Test internal class AccessorHolderTest : GearyTest() { - class FancyQuery : GearyQuery() { - val Pointer.default by get().orDefault { "empty!" } - val Pointer.mapped by get().map { it.toString() } + fun fancyQuery() = object : Query() { + val default by target.get().orDefault { "empty!" } + val mapped by target.get().map { it.toString() } } @ExperimentalCoroutinesApi @@ -17,6 +17,7 @@ internal class AccessorHolderTest : GearyTest() { fun fancyAccessors() { val entity = entity() entity.set(1) + //TODO reimplement test // FancyQuery.firstOrNull { it.entity == entity } shouldBe null // entity.set(1) // FancyQuery.run { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt index f727150ed..305b9cd67 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt @@ -4,14 +4,17 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.helpers.Comp1 import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.cachedQuery import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe import kotlin.test.Test -class RemovableAccessorTest: GearyTest() { - class MyQueryRemovable : Query() { - var Pointer.data by get().removable() - } +class RemovableAccessorTest : GearyTest() { + private fun createRemovableQuery() = geary.cachedQuery(object : Query() { + var data by target.get().removable() + }) + @OptIn(UnsafeAccessors::class) @Test @@ -22,14 +25,12 @@ class RemovableAccessorTest: GearyTest() { } var count = 0 - MyQueryRemovable().run { - forEach { - it.data shouldBe Comp1(1) - it.data = null - it.data shouldBe null - it.entity.has() shouldBe false - count++ - } + createRemovableQuery().forEach { + data shouldBe Comp1(1) + data = null + data shouldBe null + target.entity.has() shouldBe false + count++ } count shouldBe 1 } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt index d73a70462..375b85c96 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt @@ -4,15 +4,16 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.helpers.Comp1 import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.cachedQuery import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe import kotlin.test.Test class UnsafeQueryAccessTests : GearyTest() { - class MyQuery : Query() { - var Pointer.data by get() - } - + private fun registerQuery() = geary.cachedQuery(object : Query() { + var data by target.get() + }) @Test fun `should allow data modify via accessor`() { @@ -22,13 +23,11 @@ class UnsafeQueryAccessTests : GearyTest() { } var count = 0 - MyQuery().run { - forEach { - it.data shouldBe Comp1(1) - it.data = Comp1(10) - it.data shouldBe Comp1(10) - count++ - } + registerQuery().forEach { + data shouldBe Comp1(1) + data = Comp1(10) + data shouldBe Comp1(10) + count++ } count shouldBe 1 } @@ -42,15 +41,13 @@ class UnsafeQueryAccessTests : GearyTest() { } var count = 0 - MyQuery().run { - forEach { - it.data shouldBe Comp1(1) - it.data = Comp1(10) - it.entity.set("Other comp") - it.entity.add() - it.data shouldBe Comp1(10) - count++ - } + registerQuery().forEach { + data shouldBe Comp1(1) + data = Comp1(10) + target.entity.set("Other comp") + target.entity.add() + data shouldBe Comp1(10) + count++ } count shouldBe 1 } @@ -64,18 +61,15 @@ class UnsafeQueryAccessTests : GearyTest() { } var count = 0 - MyQuery().run { - forEach { - it.data shouldBe Comp1(1) - it.entity.remove() - it.data = Comp1(10) - it.data shouldBe Comp1(10) - it.entity.set("Other comp") - it.data shouldBe Comp1(10) - count++ - } + registerQuery().forEach { + data shouldBe Comp1(1) + target.entity.remove() + data = Comp1(10) + data shouldBe Comp1(10) + target.entity.set("Other comp") + data shouldBe Comp1(10) + count++ } count shouldBe 1 } - } From a44d831aa7b34f7cfa14e256e63f40c6af6ddd0c Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Fri, 23 Feb 2024 00:38:58 +0100 Subject: [PATCH 05/14] Use `this` instead of target for referencing current queried entity - expand upon accessorholder for events --- .../systems/ParseChildOnPrefab.kt | 14 ++++---- .../geary/uuid/systems/TrackUuidOnAdd.kt | 13 +++---- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 5 +-- .../benchmarks/VelocitySystemBenchmark.kt | 12 +++---- .../engine/archetypes/ArchetypeEventRunner.kt | 6 ++-- .../geary/systems/ListenerBuilder.kt | 3 +- .../com/mineinabyss/geary/systems/System.kt | 2 +- .../geary/systems/SystemBuilder.kt | 36 +++++++++---------- .../systems/accessors/AccessorOperations.kt | 26 +++++++++++--- .../geary/systems/query/CachedQueryRunner.kt | 4 +-- .../geary/systems/query/ListenerQuery.kt | 33 ++++++----------- .../geary/systems/query/QueriedEntity.kt | 9 ++--- .../mineinabyss/geary/systems/query/Query.kt | 3 +- .../geary/events/ComponentAddEventTest.kt | 11 +++--- .../geary/systems/FamilyMatchingTest.kt | 2 +- .../systems/RelationMatchingSystemTest.kt | 6 ++-- 16 files changed, 92 insertions(+), 93 deletions(-) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt index 5ba8fe7cd..6a61460ea 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt @@ -15,10 +15,11 @@ import com.mineinabyss.geary.systems.query.ListenerQuery @OptIn(UnsafeAccessors::class) fun createParseChildOnPrefabListener() = geary.listener(object : ListenerQuery() { - var child by target.get().removable() -}.apply { onSet(::child) }).exec { + var child by get().removable() + override fun ensure() = event.anySet(::child) +}).exec { entity { - addParent(target.entity) + addParent(entity) setAll(child!!.components) } child = null @@ -26,13 +27,14 @@ fun createParseChildOnPrefabListener() = geary.listener(object : ListenerQuery() @OptIn(UnsafeAccessors::class) fun createParseChildrenOnPrefabListener() = geary.listener(object : ListenerQuery() { - var children by target.get().removable() -}.apply { onSet(::children) }).exec { + var children by get().removable() + override fun ensure() = event.anySet(::children) +}).exec { children!!.nameToComponents.forEach { (name, components) -> entity { set(EntityName(name)) set(Prefab()) - addParent(target.entity) + addParent(entity) addRelation() setAll(components) } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt index 7d867b4f0..de54bbc9c 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt @@ -11,15 +11,16 @@ import com.mineinabyss.geary.uuid.uuid2Geary @OptIn(UnsafeAccessors::class) fun createTrackUUIDOnAddSystem() = geary.listener(object : ListenerQuery() { - var uuid by target.get() - val regenerateUUIDOnClash by target.get().orNull() -}.apply { onSet(::uuid) }).exec { + var uuid by get() + val regenerateUUIDOnClash by get().orNull() + override fun ensure() = event.anySet(::uuid) +}).exec { if (uuid in uuid2Geary) if (regenerateUUIDOnClash != null) { val newUuid = uuid4() uuid = newUuid - uuid2Geary[newUuid] = target.entity - } else error("Tried tracking entity $target.entity with already existing uuid $uuid") + uuid2Geary[newUuid] = entity + } else error("Tried tracking entity $entity with already existing uuid $uuid") else - uuid2Geary[uuid] = target.entity + uuid2Geary[uuid] = entity } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index 8c222899f..965d64058 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -9,7 +9,8 @@ import com.mineinabyss.geary.uuid.uuid2Geary //TODO fix fun createUntrackUuidOnRemoveSystem() = geary.listener(object : ListenerQuery() { - val uuid by target.get() -}.apply { event.match { has() } }) { + val uuid by get() + override fun ensure() = event.match { has() } +}).exec { uuid2Geary.remove(uuid) } diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt index 31ac78934..eb62e2fd4 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt @@ -18,13 +18,11 @@ class VelocitySystemBenchmark { data class Position(var x: Float, var y: Float) val velocitySystem = geary.system(object : Query() { - val velocity by target.get() - var position by target.get() - }) { - onTick { - position.x += velocity.x - position.y += velocity.y - } + val velocity by get() + var position by get() + }).exec { + position.x += velocity.x + position.y += velocity.y } val velocities = Array(tenMil) { Velocity(it.toFloat() / oneMil, it.toFloat() / oneMil) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt index e9cfa8df0..9c3b71a1f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt @@ -26,9 +26,9 @@ class ArchetypeEventRunner : EventRunner { query.event.delegated = false query.event.originalArchetype = event.archetype query.event.originalRow = event.row - query.target.delegated = false - query.target.originalArchetype = target.archetype - query.target.originalRow = target.row + query.delegated = false + query.originalArchetype = target.archetype + query.originalRow = target.row if (source != null) { query.source.delegated = false query.source.originalArchetype = source.archetype diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt index cdebf3c81..eec9d322d 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt @@ -14,7 +14,7 @@ fun GearyModule.listener( return ListenerBuilder(query, pipeline) } -class ListenerBuilder( +class ListenerBuilder( val query: T, val pipeline: Pipeline, ) { @@ -29,6 +29,7 @@ class ListenerBuilder( @OptIn(UnsafeAccessors::class) fun check(check: T.() -> Boolean): Listener<*> { + query.initialize() val families = query.buildFamilies() val listener = Listener( query, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt index aa26f4358..c321eb89f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt @@ -4,7 +4,7 @@ import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.Query import kotlin.time.Duration -class System internal constructor( +class System @PublishedApi internal constructor( val query: Query, val onTick: CachedQueryRunner<*>.() -> Unit, val interval: Duration, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt index 67d527a46..89ff6d6c8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt @@ -1,35 +1,33 @@ package com.mineinabyss.geary.systems +import com.mineinabyss.geary.engine.Pipeline import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.Query import kotlin.time.Duration -inline fun GearyModule.system( - query: T, - init: SystemBuilder.() -> Unit -): TrackedSystem { - val system = SystemBuilder(query).apply(init).build() - return pipeline.addSystem(system) +fun GearyModule.system( + query: T +): SystemBuilder { + return SystemBuilder(query, pipeline) } -class SystemBuilder(val query: T) { - @PublishedApi - internal var onTick: CachedQueryRunner.() -> Unit = {} +class SystemBuilder(val query: T, val pipeline: Pipeline) { var interval: Duration = Duration.ZERO - inline fun onTick(crossinline run: T.() -> Unit) { - onTick = { forEach(run) } + inline fun exec(crossinline run: T.() -> Unit): TrackedSystem { + val onTick: CachedQueryRunner.() -> Unit = { forEach(run) } + val system = System( + query, + onTick as CachedQueryRunner<*>.() -> Unit, + interval + ) + return pipeline.addSystem(system) } - fun onTickAll(run: CachedQueryRunner.() -> Unit) { - onTick = run - } +// fun onTickAll(run: CachedQueryRunner.() -> Unit) { +// onTick = run +// } - fun build(): System = System( - query, - onTick as CachedQueryRunner<*>.() -> Unit, - interval - ) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index be2b5dbba..71d7d0bae 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -8,17 +8,18 @@ import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.systems.accessors.type.* +import com.mineinabyss.geary.systems.query.EventQueriedEntity import com.mineinabyss.geary.systems.query.QueriedEntity import kotlin.reflect.KProperty open class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ - inline fun QueriedEntity.get(): ComponentAccessor { + protected inline fun QueriedEntity.get(): ComponentAccessor { return NonNullComponentAccessor(this, componentId().withRole(HOLDS_DATA)) } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ - inline fun QueriedEntity.getRelation(): ComponentAccessor { + protected inline fun QueriedEntity.getRelation(): ComponentAccessor { return NonNullComponentAccessor(this, Relation.of().id) } @@ -68,17 +69,32 @@ open class AccessorOperations { * - One of [K] or [T] is [Any] => gets all relations matching the other (specified) type. * - Note: nullability rules are still upheld with [Any]. */ - inline fun QueriedEntity.getRelations(): RelationsAccessor { + protected inline fun QueriedEntity.getRelations(): RelationsAccessor { return RelationsAccessor(this, componentIdWithNullable(), componentIdWithNullable()) } /** @see getRelations */ - inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { + protected inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { return RelationsWithDataAccessor(this, componentIdWithNullable(), componentIdWithNullable()) } - fun QueriedEntity.match(init: MutableFamily.Selector.And.() -> Unit) { + protected fun QueriedEntity.match(init: MutableFamily.Selector.And.() -> Unit) { val family = com.mineinabyss.geary.datatypes.family.family(init) extraFamilies.add(family) } + + /** Fires when an entity has a component of type [T] set or updated. */ + protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { + val names = props.map { it.name }.toSet() + match { + this@anySet.props.filterKeys { prop -> + prop.name in names + }.values.flatMap { + it.components + }.forEach { component -> + onSet(component) + // TODO do we error here if not, this isn't really typesafe? + } + } + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index 560517dd4..8f396a3fc 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -6,7 +6,7 @@ import com.mineinabyss.geary.helpers.fastForEachWithIndex class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() - val family = query.target.buildFamily() + val family = query.buildFamily() inline fun toList(crossinline map: T.() -> R): List { val list = mutableListOf() @@ -27,7 +27,7 @@ class CachedQueryRunner internal constructor(val query: T) { val archetype = matched[n] archetype.isIterating = true val upTo = archetype.size - val target = query.target + val target = query // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration target.originalArchetype = archetype diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index 8c58686b5..e9a61b1ae 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -1,13 +1,11 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty -abstract class ListenerQuery : AccessorOperations() { - val target: QueriedEntity = QueriedEntity() - val event: QueriedEntity = QueriedEntity() +abstract class ListenerQuery : QueriedEntity() { + val event: EventQueriedEntity = EventQueriedEntity() val source: QueriedEntity = QueriedEntity() data class Families( @@ -18,10 +16,14 @@ abstract class ListenerQuery : AccessorOperations() { fun buildFamilies(): Families = Families( event = event.buildFamily(), - target = target.buildFamily(), + target = this.buildFamily(), source = source.buildFamily(), ) + protected open fun ensure() {} + + internal fun initialize() = ensure() + /** Automatically matches families for any accessor that's supposed to match a family. */ operator fun T.provideDelegate( thisRef: Any, @@ -32,33 +34,18 @@ abstract class ListenerQuery : AccessorOperations() { } /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - fun onExtend() { + protected fun onExtend() { event.match { onExtendedEntity() } // return getRelations().map { it.single().target.toGeary() }.on(event) } /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - fun onAdd(vararg props: KProperty<*>) { - - } + protected fun onAdd(vararg props: KProperty<*>) { - /** Fires when an entity has a component of type [T] set or updated. */ - fun onSet(vararg props: KProperty<*>) { - val names = props.map { it.name }.toSet() - event.match { - target.props.filterKeys { prop -> - prop.name in names - }.values.flatMap { - it.components - }.forEach { component -> - onSet(component) - // TODO do we error here if not, this isn't really typesafe? - } - } } /** Fires when an entity has a component of type [T] set, only if it was not set before. */ - fun onFirstSet(vararg props: KProperty<*>) { + protected fun onFirstSet(vararg props: KProperty<*>) { } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index f8caf1cc0..f3ac26f9f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -7,9 +7,11 @@ import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.systems.accessors.AccessorOperations import kotlin.reflect.KProperty -class QueriedEntity { +open class EventQueriedEntity: QueriedEntity() +open class QueriedEntity: AccessorOperations() { internal val extraFamilies: MutableList = mutableListOf() internal val props: MutableMap, Family.Selector> = mutableMapOf() @@ -49,9 +51,4 @@ class QueriedEntity { delegated = true return entity } - - @UnsafeAccessors - operator fun component1(): Archetype = archetype - - operator fun component2(): Int = row } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index 2b91bcb64..ee0018a8b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -4,8 +4,7 @@ import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty -abstract class Query : AccessorOperations() { - val target: QueriedEntity = QueriedEntity() +abstract class Query: QueriedEntity() { //TODO duplicate with EventQuery /** Automatically matches families for any accessor that's supposed to match a family. */ diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index 15d28bdcb..4b37abb75 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -13,12 +13,11 @@ internal class ComponentAddEventTest : GearyTest() { var inc = 0 fun onStringAdd() = geary.listener(object : ListenerQuery() { - val string by target.get() - val int by target.get() - val double by target.get() - }.apply { onSet(::string, ::int, ::double) }) { - inc++ - } + val string by get() + val int by get() + val double by get() + }.apply { onSet(::string, ::int, ::double) }) + .exec { inc++ } @Test fun componentAddEvent() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index 2013bb19e..c07f0e2f9 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -21,7 +21,7 @@ class FamilyMatchingTest : GearyTest() { val system = geary.system(object : Query() { val string by target.get() }.apply { target.match { has() } }) { - onTick { + exec { string shouldBe target.entity.get() target.entity.has() shouldBe true } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt index 09cbf0eb3..bae1e689d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt @@ -25,7 +25,7 @@ class RelationMatchingSystemTest : GearyTest() { val system = geary.system(object : Query() { val persists by target.getRelationsWithData() }) { - onTick { + exec { ran++ persists.forAll { it.data.shouldBeInstanceOf() } } @@ -61,7 +61,7 @@ class RelationMatchingSystemTest : GearyTest() { val persists by target.getRelationsWithData() val instanceOf by target.getRelationsWithData() }) { - onTick { + exec { ran++ persistsCount += persists.size instanceOfCount += instanceOf.size @@ -115,7 +115,7 @@ class RelationMatchingSystemTest : GearyTest() { val system = geary.system(object : Query() { val withData by target.getRelationsWithData() }) { - onTick { + exec { withData.forAll { it.data shouldBe Persists() } withData.forAll { it.targetData shouldBe "Test" } } From 42523fc4c1097b066946e2e4d9db8c64774f0309 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sat, 24 Feb 2024 18:18:14 +0100 Subject: [PATCH 06/14] Update more tests and usage to new syntax --- .../com/mineinabyss/geary/prefabs/Prefabs.kt | 14 ++++---- .../systems/CopyToInstancesSystem.kt | 19 ++++++----- .../systems/ParseRelationOnPrefab.kt | 23 +++++++------ .../systems/ParseRelationWithDataSystem.kt | 32 +++++++++---------- .../prefabs/systems/InheritPrefabsOnLoad.kt | 20 ++++-------- .../systems/TrackPrefabsByKeySystem.kt | 22 ++++++------- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 2 +- .../systems/accessors/AccessorOperations.kt | 18 +++++++---- .../geary/systems/query/ListenerQuery.kt | 10 +++--- .../geary/events/CheckingListenerTest.kt | 5 ++- .../geary/events/ComponentAddEventTest.kt | 4 +-- .../events/SimpleComponentAddListenerTest.kt | 5 +-- .../geary/events/SimpleEventTest.kt | 10 ++---- .../geary/events/SourceTargetEventTest.kt | 5 +-- .../geary/queries/SimpleQueryTest.kt | 2 +- .../geary/systems/FamilyMatchingTest.kt | 12 +++---- .../geary/systems/QueryManagerTest.kt | 4 +-- .../systems/RelationMatchingSystemTest.kt | 26 +++++++-------- .../systems/accessors/AccessorHolderTest.kt | 4 +-- .../accessors/RemovableAccessorTest.kt | 2 +- .../accessors/UnsafeQueryAccessTests.kt | 6 ++-- 21 files changed, 115 insertions(+), 130 deletions(-) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt index 47a4264d1..f86334c55 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt @@ -6,7 +6,8 @@ import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.systems.* -import com.mineinabyss.geary.prefabs.systems.TrackPrefabsByKeySystem +import com.mineinabyss.geary.prefabs.systems.createInheritPrefabsOnLoadListener +import com.mineinabyss.geary.prefabs.systems.createTrackPrefabsByKeyListener import com.mineinabyss.idofront.di.DI val prefabs by DI.observe() @@ -23,14 +24,13 @@ interface Prefabs { override fun Prefabs.install() { + createInheritPrefabsOnLoadListener() createParseChildOnPrefabListener() createParseChildrenOnPrefabListener() - geary.pipeline.addSystems( - ParseRelationOnPrefab(), - ParseRelationWithDataSystem(), - TrackPrefabsByKeySystem(), - CopyToInstancesSystem(), - ) + createParseRelationOnPrefabListener() + createParseRelationWithDataListener() + createTrackPrefabsByKeyListener() + createCopyToInstancesSystem() geary.pipeline.runOnOrAfter(GearyPhase.INIT_ENTITIES) { loader.loadOrUpdatePrefabs() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt index e9737014e..276c94500 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt @@ -1,16 +1,15 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances -import com.mineinabyss.geary.systems.GearyListener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery -class CopyToInstancesSystem : GearyListener() { - private val Pointers.baseEntity by whenExtendedEntity() - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - val copy = baseEntity.get() ?: return - copy.decodeComponentsTo(target.entity) - } +@OptIn(UnsafeAccessors::class) +fun createCopyToInstancesSystem() = geary.listener(object : ListenerQuery() { + val baseEntity by event.extendedEntity() +}).exec { + val copy = baseEntity.get() ?: return@exec + copy.decodeComponentsTo(entity) } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt index c21f15929..907f05ee6 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt @@ -1,20 +1,19 @@ package com.mineinabyss.geary.prefabs.configuration.systems +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery -class ParseRelationOnPrefab : Listener() { - private var Pointers.relation by get().removable().whenSetOnTarget() - - override fun Pointers.handle() { - try { - val rel: RelationOnPrefab = relation!! +fun createParseRelationOnPrefabListener() = geary.listener(object : ListenerQuery() { + var relation by get().removable() + override fun ensure() = event.anySet(::relation) +}).exec { + try { + val rel: RelationOnPrefab = relation!! // entity.setRelation(relation.value, entity.parseEntity(relation.key).id) - } finally { - relation = null - } + } finally { + relation = null } } - diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt index 8b048fd4f..2bed40bc2 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt @@ -1,23 +1,21 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.Records -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.accessors.RelationWithData +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery - -class ParseRelationWithDataSystem : Listener() { - private val Records.relationWithData by get>().whenSetOnTarget() - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - val entity = target.entity - val data = relationWithData.data - val targetData = relationWithData.targetData - if (data != null) entity.set(data, relationWithData.relation.id) - else entity.add(relationWithData.relation.id) - if (targetData != null) entity.set(targetData, relationWithData.target.id) - entity.remove>() - } +@OptIn(UnsafeAccessors::class) +fun createParseRelationWithDataListener() = geary.listener(object : ListenerQuery() { + val relationWithData by get>() + override fun ensure() = event.anySet(::relationWithData) +}).exec { + val entity = entity + val data = relationWithData.data + val targetData = relationWithData.targetData + if (data != null) entity.set(data, relationWithData.relation.id) + else entity.add(relationWithData.relation.id) + if (targetData != null) entity.set(targetData, relationWithData.target.id) + entity.remove>() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt index e73fc8c68..c6d887335 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt @@ -1,19 +1,13 @@ package com.mineinabyss.geary.prefabs.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.events.PrefabLoaded import com.mineinabyss.geary.prefabs.helpers.inheritPrefabs -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers - - -class InheritPrefabsOnLoad : Listener() { - private val Pointers.loaded by family { has() }.on(event) - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - target.entity.inheritPrefabs() - } -} +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery +@OptIn(UnsafeAccessors::class) +fun createInheritPrefabsOnLoadListener() = geary.listener(object : ListenerQuery() { + override fun ensure() = event { has() } +}).exec { entity.inheritPrefabs() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt index 9977c96da..d3e41349f 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt @@ -2,19 +2,17 @@ package com.mineinabyss.geary.prefabs.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.relations.NoInherit -import com.mineinabyss.geary.datatypes.Records +import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.PrefabKey import com.mineinabyss.geary.prefabs.prefabs -import com.mineinabyss.geary.systems.Listener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery - -class TrackPrefabsByKeySystem : Listener() { - private val Records.key by get().whenSetOnTarget() - - @OptIn(UnsafeAccessors::class) - override fun Pointers.handle() { - prefabs.manager.registerPrefab(key, target.entity) - target.entity.addRelation() - } +@OptIn(UnsafeAccessors::class) +fun createTrackPrefabsByKeyListener() = geary.listener(object : ListenerQuery() { + val key by get() + override fun ensure() = event.anySet(::key) +}).exec { + prefabs.manager.registerPrefab(key, entity) + entity.addRelation() } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index 965d64058..be48bb81a 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -10,7 +10,7 @@ import com.mineinabyss.geary.uuid.uuid2Geary //TODO fix fun createUntrackUuidOnRemoveSystem() = geary.listener(object : ListenerQuery() { val uuid by get() - override fun ensure() = event.match { has() } + override fun ensure() = event.invoke { has() } }).exec { uuid2Geary.remove(uuid) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 71d7d0bae..a6fab42cd 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -1,12 +1,11 @@ package com.mineinabyss.geary.systems.accessors -import com.mineinabyss.geary.datatypes.Component -import com.mineinabyss.geary.datatypes.HOLDS_DATA -import com.mineinabyss.geary.datatypes.Relation +import com.mineinabyss.geary.components.events.ExtendedEntity +import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.family.MutableFamily -import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable +import com.mineinabyss.geary.helpers.toGeary import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.EventQueriedEntity import com.mineinabyss.geary.systems.query.QueriedEntity @@ -78,7 +77,7 @@ open class AccessorOperations { return RelationsWithDataAccessor(this, componentIdWithNullable(), componentIdWithNullable()) } - protected fun QueriedEntity.match(init: MutableFamily.Selector.And.() -> Unit) { + protected operator fun QueriedEntity.invoke(init: MutableFamily.Selector.And.() -> Unit) { val family = com.mineinabyss.geary.datatypes.family.family(init) extraFamilies.add(family) } @@ -86,7 +85,7 @@ open class AccessorOperations { /** Fires when an entity has a component of type [T] set or updated. */ protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { val names = props.map { it.name }.toSet() - match { + invoke { this@anySet.props.filterKeys { prop -> prop.name in names }.values.flatMap { @@ -97,4 +96,11 @@ open class AccessorOperations { } } } + + + /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ + protected fun EventQueriedEntity.extendedEntity(): ReadOnlyAccessor { + invoke { onExtendedEntity() } + return getRelations().map { it.single().target.toGeary() } + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index e9a61b1ae..2c2487bd8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -1,7 +1,11 @@ package com.mineinabyss.geary.systems.query +import com.mineinabyss.geary.components.events.ExtendedEntity +import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.helpers.toGeary import com.mineinabyss.geary.systems.accessors.FamilyMatching +import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import kotlin.reflect.KProperty abstract class ListenerQuery : QueriedEntity() { @@ -33,12 +37,6 @@ abstract class ListenerQuery : QueriedEntity() { return this } - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - protected fun onExtend() { - event.match { onExtendedEntity() } -// return getRelations().map { it.single().target.toGeary() }.on(event) - } - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ protected fun onAdd(vararg props: KProperty<*>) { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt index 9ef639bab..37d9afd33 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt @@ -3,7 +3,6 @@ package com.mineinabyss.geary.events import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.checkingListener import com.mineinabyss.geary.systems.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe @@ -12,8 +11,8 @@ import kotlin.test.Test class CheckingListenerTest : GearyTest() { class MyEvent() - fun myListener() = geary.listener(object: ListenerQuery() { - val data by target.get() + fun myListener() = geary.listener(object : ListenerQuery() { + val data by get() }).check { data > 10 } @Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index 4b37abb75..8278deeb9 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -16,8 +16,8 @@ internal class ComponentAddEventTest : GearyTest() { val string by get() val int by get() val double by get() - }.apply { onSet(::string, ::int, ::double) }) - .exec { inc++ } + override fun ensure() = event.anySet(::string, ::int, ::double) + }).exec { inc++ } @Test fun componentAddEvent() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt index 4dcb60573..3da4027c2 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt @@ -11,8 +11,9 @@ import kotlin.test.Test class SimpleComponentAddListenerTest : GearyTest() { var called = 0 fun myListener() = geary.listener(object : ListenerQuery() { - val data by target.get() - }.apply { onSet(::data) }) { called += 1 } + val data by get() + override fun ensure() = event.anySet(::data) + }).exec { called += 1 } @Test fun `simple event listener`() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt index 292a17474..e2fdceed9 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt @@ -14,18 +14,14 @@ class SimpleEventTest : GearyTest() { var called = 0 fun myListener() = geary.listener(object : ListenerQuery() { - val data by target.get() + val data by get() val myEvent by event.get() - }) { - called++ - } + }).exec { called++ } fun sourceOnlyListener() = geary.listener(object : ListenerQuery() { val data by source.get() val myEvent by event.get() - }) { - called++ - } + }).exec { called++ } @Test fun `simple set listener`() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt index b89199ec9..19d918058 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt @@ -15,8 +15,9 @@ class SourceTargetEventTest : GearyTest() { fun interactionListener() = geary.listener(object : ListenerQuery() { val strength by source.get() - var health by target.get() - }.apply { event.match { has() } }) { + var health by get() + override fun ensure() = event { has()} + }).exec { health = Health(health.amount - strength.amount) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt index 083c5b844..59ca95e62 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt @@ -9,7 +9,7 @@ import kotlin.test.Test class SimpleQueryTest : GearyTest() { class MyQuery : Query() { - val int by target.get() + val int by get() } @Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index c07f0e2f9..aafef8437 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -19,12 +19,12 @@ class FamilyMatchingTest : GearyTest() { @OptIn(UnsafeAccessors::class) val system = geary.system(object : Query() { - val string by target.get() - }.apply { target.match { has() } }) { - exec { - string shouldBe target.entity.get() - target.entity.has() shouldBe true - } + val string by get() + ensure // TODO +// { target.match { has() } } + }).exec { + string shouldBe entity.get() + entity.has() shouldBe true } val root = archetypes.archetypeProvider.rootArchetype diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt index f494d79ea..6895682fe 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt @@ -15,8 +15,8 @@ internal class QueryManagerTest : GearyTest() { var ran = 0 fun myListener() = geary.listener(object : ListenerQuery() { - val testComponent by target.get() - }) { ran++ } + val testComponent by get() + }).exec { ran++ } @Test fun `empty event handler`() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt index bae1e689d..84f1e9dac 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt @@ -23,12 +23,10 @@ class RelationMatchingSystemTest : GearyTest() { var ran = 0 resetEngine() val system = geary.system(object : Query() { - val persists by target.getRelationsWithData() - }) { - exec { - ran++ - persists.forAll { it.data.shouldBeInstanceOf() } - } + val persists by getRelationsWithData() + }).exec { + ran++ + persists.forAll { it.data.shouldBeInstanceOf() } } val entity = entity { @@ -60,15 +58,13 @@ class RelationMatchingSystemTest : GearyTest() { val system = geary.system(object : Query() { val persists by target.getRelationsWithData() val instanceOf by target.getRelationsWithData() - }) { - exec { - ran++ - persistsCount += persists.size - instanceOfCount += instanceOf.size - persists.forAll { it.data.shouldBeInstanceOf() } - persists.forAll { it.targetData shouldNotBe null } - instanceOf.forAll { it.data shouldBe null } - } + }).exec { + ran++ + persistsCount += persists.size + instanceOfCount += instanceOf.size + persists.forAll { it.data.shouldBeInstanceOf() } + persists.forAll { it.targetData shouldNotBe null } + instanceOf.forAll { it.data shouldBe null } } entity { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt index 0ddd6d48b..62addef15 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt @@ -8,8 +8,8 @@ import org.junit.jupiter.api.Test internal class AccessorHolderTest : GearyTest() { fun fancyQuery() = object : Query() { - val default by target.get().orDefault { "empty!" } - val mapped by target.get().map { it.toString() } + val default by get().orDefault { "empty!" } + val mapped by get().map { it.toString() } } @ExperimentalCoroutinesApi diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt index 305b9cd67..0ae881bb7 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt @@ -12,7 +12,7 @@ import kotlin.test.Test class RemovableAccessorTest : GearyTest() { private fun createRemovableQuery() = geary.cachedQuery(object : Query() { - var data by target.get().removable() + var data by get().removable() }) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt index 375b85c96..0881bfcae 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt @@ -12,7 +12,7 @@ import kotlin.test.Test class UnsafeQueryAccessTests : GearyTest() { private fun registerQuery() = geary.cachedQuery(object : Query() { - var data by target.get() + var data by get() }) @Test @@ -44,8 +44,8 @@ class UnsafeQueryAccessTests : GearyTest() { registerQuery().forEach { data shouldBe Comp1(1) data = Comp1(10) - target.entity.set("Other comp") - target.entity.add() + entity.set("Other comp") + entity.add() data shouldBe Comp1(10) count++ } From ca7857101f1b056d8a6985717037493bfd2923fd Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sat, 24 Feb 2024 12:46:16 -0500 Subject: [PATCH 07/14] Compiles again --- .../geary/benchmarks/events/EventCalls.kt | 19 ++-- .../geary/benchmarks/unpacking/Systems.kt | 48 ++++++++++ .../benchmarks/unpacking/Unpack1Benchmark.kt | 12 +-- .../benchmarks/unpacking/Unpack2Benchmark.kt | 25 ++---- .../benchmarks/unpacking/Unpack6Benchmark.kt | 87 +++++-------------- .../mineinabyss/geary/engine/PipelineImpl.kt | 1 + .../archetypes/ArchetypeQueryManager.kt | 1 + .../geary/systems/SystemBuilder.kt | 12 ++- .../mineinabyss/geary/systems/query/Query.kt | 7 +- .../geary/systems/FamilyMatchingTest.kt | 3 +- .../systems/RelationMatchingSystemTest.kt | 14 ++- .../accessors/RemovableAccessorTest.kt | 2 +- .../accessors/UnsafeQueryAccessTests.kt | 4 +- 13 files changed, 116 insertions(+), 119 deletions(-) create mode 100644 geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt index d1e8458ca..dfbbf8385 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt @@ -5,8 +5,8 @@ import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.GearyListener -import com.mineinabyss.geary.systems.accessors.Pointers +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.idofront.di.DI import org.openjdk.jmh.annotations.* @@ -14,6 +14,7 @@ import org.openjdk.jmh.annotations.* class EventCalls { var targets = emptyList() + @Setup(Level.Invocation) fun setupPerInvocation() { geary(TestEngineModule) @@ -25,13 +26,13 @@ class EventCalls { DI.clear() } - private class Listener: GearyListener() { - val Pointers.int by get().on(target) - val Pointers.event by get().on(event) - var count = 0 - override fun Pointers.handle() { - count++ - } + var count = 0 + + fun createListener() = geary.listener(object : ListenerQuery() { + val int by get() + val eventComp by event.get() + }).exec { + count++ } private class Event diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt new file mode 100644 index 000000000..3e2a02b1c --- /dev/null +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt @@ -0,0 +1,48 @@ +package com.mineinabyss.geary.benchmarks.unpacking + +import com.mineinabyss.geary.benchmarks.helpers.* +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.cachedQuery +import com.mineinabyss.geary.systems.query.GearyQuery + +class Query1 : GearyQuery() { + val comp1 by get() +} + +class Query2 : GearyQuery() { + val comp1 by get() + val comp2 by get() +} + +class Query6 : GearyQuery() { + val comp1 by get() + val comp2 by get() + val comp3 by get() + val comp4 by get() + val comp5 by get() + val comp6 by get() +} + + +class Query6WithoutDelegate : GearyQuery() { + val comp1 = get() + val comp2 = get() + val comp3 = get() + val comp4 = get() + val comp5 = get() + val comp6 = get() + + override fun ensure() = this { + hasSet() + hasSet() + hasSet() + hasSet() + hasSet() + hasSet() + } +} + +fun systemOf1() = geary.cachedQuery(Query1()) +fun systemOf2() = geary.cachedQuery(Query2()) +fun systemOf6() = geary.cachedQuery(Query6()) +fun systemOf6WithoutDelegate() = geary.cachedQuery(Query6WithoutDelegate()) diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt index 557a4c7c3..1422ab1fa 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt @@ -5,8 +5,9 @@ import com.mineinabyss.geary.benchmarks.helpers.tenMil import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer +import com.mineinabyss.geary.systems.cachedQuery import com.mineinabyss.geary.systems.query.GearyQuery +import com.mineinabyss.geary.systems.system import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup @@ -14,9 +15,6 @@ import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) class Unpack1Benchmark { - private object SystemOf1 : GearyQuery() { - val Pointer.comp1 by get() - } @Setup fun setUp() { @@ -31,10 +29,8 @@ class Unpack1Benchmark { @Benchmark fun unpack1of1Comp() { - SystemOf1.run { - forEach { - it.comp1 - } + systemOf1().forEach { + comp1 } } } diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack2Benchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack2Benchmark.kt index 340e0a13f..99d77c145 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack2Benchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack2Benchmark.kt @@ -6,8 +6,6 @@ import com.mineinabyss.geary.benchmarks.helpers.tenMil import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer -import com.mineinabyss.geary.systems.query.GearyQuery import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup @@ -15,15 +13,6 @@ import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) class Unpack2Benchmark { - private object SystemOf2 : GearyQuery() { - val Pointer.comp1 by get() - val Pointer.comp2 by get() - } - - private object SystemOf1 : GearyQuery() { - val Pointer.comp1 by get() - } - @Setup fun setUp() { geary(TestEngineModule) { @@ -39,20 +28,16 @@ class Unpack2Benchmark { @Benchmark fun unpack1of2Comp() { - SystemOf1.run { - forEach { - it.comp1 - } + systemOf1().forEach { + comp1 } } @Benchmark fun unpack2of2Comp() { - SystemOf2.run { - forEach { - it.comp1 - it.comp2 - } + systemOf2().forEach { + comp1 + comp2 } } } diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt index 1c225b916..fa5403469 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt @@ -1,12 +1,9 @@ package com.mineinabyss.geary.benchmarks.unpacking import com.mineinabyss.geary.benchmarks.helpers.* -import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.accessors.Pointer -import com.mineinabyss.geary.systems.query.GearyQuery import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup @@ -14,42 +11,10 @@ import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) class Unpack6Benchmark { - private object SystemOf6 : GearyQuery() { - val Pointer.comp1 by get() - val Pointer.comp2 by get() - val Pointer.comp3 by get() - val Pointer.comp4 by get() - val Pointer.comp5 by get() - val Pointer.comp6 by get() - - } - - private object SystemOf6WithoutDelegate : GearyQuery() { - val comp1 = get() - val comp2 = get() - val comp3 = get() - val comp4 = get() - val comp5 = get() - val comp6 = get() - - val test by family { - hasSet() - hasSet() - hasSet() - hasSet() - hasSet() - hasSet() - } - } - - private object SystemOf1 : GearyQuery() { - val Pointer.comp1 by get() - } @Setup fun setUp() { - geary(TestEngineModule) { - } + geary(TestEngineModule) repeat(tenMil) { entity { @@ -65,43 +30,39 @@ class Unpack6Benchmark { @Benchmark fun unpack1of6Comp() { - SystemOf1.run { - forEach { - it.comp1 - } + systemOf1().forEach { + comp1 } } @Benchmark fun unpack6of6Comp() { - SystemOf6.run { - forEach { - it.comp1 - it.comp2 - it.comp3 - it.comp4 - it.comp5 - it.comp6 - } + systemOf6().forEach { + comp1 + comp2 + comp3 + comp4 + comp5 + comp6 } } - @Benchmark - fun unpack6of6CompNoDelegate() { - SystemOf6WithoutDelegate.run { - forEach { - comp1[it] - comp2[it] - comp3[it] - comp4[it] - comp5[it] - comp6[it] - } - } - } + //TODO currently no way to avoid delegates +// @Benchmark +// fun unpack6of6CompNoDelegate() { +// systemOf6WithoutDelegate().run { +// forEach { +// comp1 +// comp2 +// comp3 +// comp4 +// comp5 +// comp6 +// } +// } +// } } - fun main() { Unpack6Benchmark().apply { setUp() diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt index c01bd87b7..bf3fedd10 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt @@ -5,6 +5,7 @@ import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.System import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.cachedQuery import com.mineinabyss.geary.systems.query.CachedQueryRunner import kotlin.time.Duration diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt index dd659b860..37b179ef6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeQueryManager.kt @@ -48,6 +48,7 @@ class ArchetypeQueryManager : QueryManager { } override fun trackQuery(query: T): CachedQueryRunner { + query.initialize() val queryRunner = CachedQueryRunner(query) val matched = archetypes.match(queryRunner.family) queryRunner.matchedArchetypes += matched diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt index 89ff6d6c8..11f818aec 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt @@ -26,8 +26,12 @@ class SystemBuilder(val query: T, val pipeline: Pipeline) { return pipeline.addSystem(system) } -// fun onTickAll(run: CachedQueryRunner.() -> Unit) { -// onTick = run -// } - + fun execOnAll(run: CachedQueryRunner.() -> Unit): TrackedSystem { + val system = System( + query, + run as CachedQueryRunner<*>.() -> Unit, + interval + ) + return pipeline.addSystem(system) + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index ee0018a8b..509e89e07 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -1,10 +1,9 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty -abstract class Query: QueriedEntity() { +abstract class Query : QueriedEntity() { //TODO duplicate with EventQuery /** Automatically matches families for any accessor that's supposed to match a family. */ @@ -15,4 +14,8 @@ abstract class Query: QueriedEntity() { family?.let { queriedEntity.props[prop] = it } return this } + + protected open fun ensure() {} + + internal fun initialize() = ensure() } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index aafef8437..cfcd5d98d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -20,8 +20,7 @@ class FamilyMatchingTest : GearyTest() { @OptIn(UnsafeAccessors::class) val system = geary.system(object : Query() { val string by get() - ensure // TODO -// { target.match { has() } } + override fun ensure() = this { has() } }).exec { string shouldBe entity.get() entity.has() shouldBe true diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt index 84f1e9dac..42d38cc26 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt @@ -56,8 +56,8 @@ class RelationMatchingSystemTest : GearyTest() { var persistsCount = 0 var instanceOfCount = 0 val system = geary.system(object : Query() { - val persists by target.getRelationsWithData() - val instanceOf by target.getRelationsWithData() + val persists by getRelationsWithData() + val instanceOf by getRelationsWithData() }).exec { ran++ persistsCount += persists.size @@ -109,12 +109,10 @@ class RelationMatchingSystemTest : GearyTest() { } val system = geary.system(object : Query() { - val withData by target.getRelationsWithData() - }) { - exec { - withData.forAll { it.data shouldBe Persists() } - withData.forAll { it.targetData shouldBe "Test" } - } + val withData by getRelationsWithData() + }).exec { + withData.forAll { it.data shouldBe Persists() } + withData.forAll { it.targetData shouldBe "Test" } } system.runner.matchedArchetypes.shouldNotContain(entity.type.getArchetype()) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt index 0ae881bb7..23c2de8a3 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt @@ -29,7 +29,7 @@ class RemovableAccessorTest : GearyTest() { data shouldBe Comp1(1) data = null data shouldBe null - target.entity.has() shouldBe false + entity.has() shouldBe false count++ } count shouldBe 1 diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt index 0881bfcae..1706e8f43 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt @@ -63,10 +63,10 @@ class UnsafeQueryAccessTests : GearyTest() { var count = 0 registerQuery().forEach { data shouldBe Comp1(1) - target.entity.remove() + entity.remove() data = Comp1(10) data shouldBe Comp1(10) - target.entity.set("Other comp") + entity.set("Other comp") data shouldBe Comp1(10) count++ } From 007799f2334e858ee0ffbab3fa3acb6d017e262b Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sat, 24 Feb 2024 14:14:36 -0500 Subject: [PATCH 08/14] All tests passing --- .../geary/autoscan/AutoscanAnnotations.kt | 1 - .../geary/systems/ListenerBuilder.kt | 1 + .../systems/accessors/AccessorOperations.kt | 35 ++++--------------- .../systems/accessors/type/MappedAccessor.kt | 12 +++++++ .../geary/systems/query/ListenerQuery.kt | 16 +++++++++ 5 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt diff --git a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoscanAnnotations.kt b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoscanAnnotations.kt index 278f18742..4ba6bb476 100644 --- a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoscanAnnotations.kt +++ b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoscanAnnotations.kt @@ -2,7 +2,6 @@ package com.mineinabyss.geary.autoscan 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 /** diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt index eec9d322d..ad1f5aeba 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt @@ -19,6 +19,7 @@ class ListenerBuilder( val pipeline: Pipeline, ) { fun exec(handle: T.() -> Unit): Listener<*> { + query.initialize() val listener = Listener( query, query.buildFamilies(), diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index a6fab42cd..8cec087cf 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -9,7 +9,6 @@ import com.mineinabyss.geary.helpers.toGeary import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.EventQueriedEntity import com.mineinabyss.geary.systems.query.QueriedEntity -import kotlin.reflect.KProperty open class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ @@ -18,7 +17,7 @@ open class AccessorOperations { } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ - protected inline fun QueriedEntity.getRelation(): ComponentAccessor { + protected inline fun QueriedEntity.getRelation(): ComponentAccessor { return NonNullComponentAccessor(this, Relation.of().id) } @@ -26,7 +25,7 @@ open class AccessorOperations { * Accesses a component, allows removing it by setting to null. * As a result, the type is nullable since it may be removed during system runtime. */ - fun ComponentAccessor.removable(): RemovableComponentAccessor { + fun ComponentAccessor.removable(): RemovableComponentAccessor { return RemovableComponentAccessor(queriedEntity, id) } @@ -40,19 +39,13 @@ open class AccessorOperations { /** Maps an accessor, will recalculate on every call. */ fun > A.map(mapping: (T) -> U): ReadOnlyAccessor { - return object : ReadOnlyAccessor, FamilyMatching { - override val queriedEntity: QueriedEntity = TODO() - override val family = (this@map as? FamilyMatching)?.family - - override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): U { - val value = this@map.getValue(thisRef, property) - return mapping(value) - } - } + return if (this is FamilyMatching) + object : ReadOnlyAccessor by MappedAccessor(this, mapping), FamilyMatching by this {} + else MappedAccessor(this, mapping) } /** Accesses a component or `null` if the entity doesn't have it. */ - fun ComponentAccessor.orNull(): ComponentOrDefaultAccessor { + fun ComponentAccessor.orNull(): ComponentOrDefaultAccessor { return orDefault { null } } @@ -82,22 +75,6 @@ open class AccessorOperations { extraFamilies.add(family) } - /** Fires when an entity has a component of type [T] set or updated. */ - protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { - val names = props.map { it.name }.toSet() - invoke { - this@anySet.props.filterKeys { prop -> - prop.name in names - }.values.flatMap { - it.components - }.forEach { component -> - onSet(component) - // TODO do we error here if not, this isn't really typesafe? - } - } - } - - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ protected fun EventQueriedEntity.extendedEntity(): ReadOnlyAccessor { invoke { onExtendedEntity() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt new file mode 100644 index 000000000..a753a599b --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt @@ -0,0 +1,12 @@ +package com.mineinabyss.geary.systems.accessors.type + +import com.mineinabyss.geary.systems.accessors.AccessorOperations +import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import kotlin.reflect.KProperty + +class MappedAccessor(val original: ReadOnlyAccessor, val mapping: (T) -> U) : ReadOnlyAccessor { + override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): U { + val value = original.getValue(thisRef, property) + return mapping(value) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index 2c2487bd8..5a1ce5df3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -46,4 +46,20 @@ abstract class ListenerQuery : QueriedEntity() { protected fun onFirstSet(vararg props: KProperty<*>) { } + + + /** Fires when an entity has a component of type [T] set or updated. */ + protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { + val names = props.map { it.name }.toSet() + invoke { + this@ListenerQuery.props.filterKeys { prop -> + prop.name in names + }.values.flatMap { + it.components + }.forEach { component -> + onSet(component) + // TODO do we error here if not, this isn't really typesafe? + } + } + } } From df1aeaefc949ef6b53f1de10887bbb88cdcc5cf4 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sun, 25 Feb 2024 17:08:05 -0500 Subject: [PATCH 09/14] Disallow modification during system iteration if accessors use cache, add deferred syntax instead --- .../systems/ParseChildOnPrefab.kt | 14 ++--- .../systems/ParseRelationOnPrefab.kt | 7 ++- geary-benchmarks/build.gradle.kts | 2 +- .../benchmarks/VelocitySystemBenchmark.kt | 20 +++++-- .../benchmarks/unpacking/Unpack6Benchmark.kt | 34 ++++++----- .../geary/systems/ListenerBuilder.kt | 1 + .../geary/systems/SystemBuilder.kt | 27 +++++++++ .../systems/accessors/AccessorOperations.kt | 37 ++++++++---- .../geary/systems/accessors/Aliases.kt | 14 ++++- .../geary/systems/accessors/FamilyMatching.kt | 1 - .../accessors/type/ComponentAccessor.kt | 59 +++++++++++-------- .../type/ComponentOrDefaultAccessor.kt | 24 ++++---- .../systems/accessors/type/MappedAccessor.kt | 12 +++- .../type/NonNullComponentAccessor.kt | 20 +++---- .../accessors/type/RelationsAccessor.kt | 5 +- .../type/RelationsWithDataAccessor.kt | 16 ++--- .../type/RemovableComponentAccessor.kt | 35 ----------- .../geary/systems/query/CachedQueryRunner.kt | 46 ++++++++++++--- .../geary/systems/query/ListenerQuery.kt | 50 +++++++--------- .../geary/systems/query/QueriedEntity.kt | 26 +++++--- .../mineinabyss/geary/systems/query/Query.kt | 19 ++++-- .../geary/events/SourceTargetEventTest.kt | 1 + .../geary/queries/SimpleQueryTest.kt | 9 ++- .../geary/systems/FamilyMatchingTest.kt | 6 +- ...=> ListenerLiveEntityModificationTests.kt} | 34 ++++++----- .../accessors/RemovableAccessorTest.kt | 37 ------------ 26 files changed, 305 insertions(+), 251 deletions(-) delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt rename geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/{UnsafeQueryAccessTests.kt => ListenerLiveEntityModificationTests.kt} (74%) delete mode 100644 geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt index 6a61460ea..f349682cc 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt @@ -13,24 +13,22 @@ import com.mineinabyss.geary.systems.listener import com.mineinabyss.geary.systems.query.ListenerQuery -@OptIn(UnsafeAccessors::class) fun createParseChildOnPrefabListener() = geary.listener(object : ListenerQuery() { - var child by get().removable() + val child by get() override fun ensure() = event.anySet(::child) }).exec { entity { addParent(entity) - setAll(child!!.components) + setAll(child.components) } - child = null + entity.remove() } -@OptIn(UnsafeAccessors::class) fun createParseChildrenOnPrefabListener() = geary.listener(object : ListenerQuery() { - var children by get().removable() + var children by get() override fun ensure() = event.anySet(::children) }).exec { - children!!.nameToComponents.forEach { (name, components) -> + children.nameToComponents.forEach { (name, components) -> entity { set(EntityName(name)) set(Prefab()) @@ -39,5 +37,5 @@ fun createParseChildrenOnPrefabListener() = geary.listener(object : ListenerQuer setAll(components) } } - children = null + entity.remove() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt index 907f05ee6..9d3ba6675 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt @@ -7,13 +7,14 @@ import com.mineinabyss.geary.systems.query.ListenerQuery fun createParseRelationOnPrefabListener() = geary.listener(object : ListenerQuery() { - var relation by get().removable() + var relation by get() override fun ensure() = event.anySet(::relation) }).exec { try { - val rel: RelationOnPrefab = relation!! + //TODO + val rel: RelationOnPrefab = relation // entity.setRelation(relation.value, entity.parseEntity(relation.key).id) } finally { - relation = null + entity.remove() } } diff --git a/geary-benchmarks/build.gradle.kts b/geary-benchmarks/build.gradle.kts index c863a1496..b264a8cae 100644 --- a/geary-benchmarks/build.gradle.kts +++ b/geary-benchmarks/build.gradle.kts @@ -45,7 +45,7 @@ benchmark { } create("specific") { - include("NewEntity") + include("Unpack6Benchmark") warmups = 1 iterations = 1 iterationTime = 3 diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt index eb62e2fd4..442f8450a 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt @@ -17,13 +17,20 @@ class VelocitySystemBenchmark { data class Velocity(val x: Float, val y: Float) data class Position(var x: Float, var y: Float) - val velocitySystem = geary.system(object : Query() { + fun createVelocitySystem() = geary.system(object : Query() { val velocity by get() var position by get() }).exec { position.x += velocity.x position.y += velocity.y } + fun createVelocitySystemNoDelegates() = geary.system(object : Query() { + val velocity = get() + var position = get() + }).exec { + position().x += velocity().x + position().y += velocity().y + } val velocities = Array(tenMil) { Velocity(it.toFloat() / oneMil, it.toFloat() / oneMil) } val positions = Array(tenMil) { Position(0f, 0f) } @@ -41,8 +48,8 @@ class VelocitySystemBenchmark { } @Benchmark - fun velocitySystem() { - velocitySystem.tick() + fun velocitySystemNoDelegates() { + createVelocitySystemNoDelegates().tick() } // Theoretical performance with zero ECS overhead @@ -55,6 +62,11 @@ class VelocitySystemBenchmark { i++ } } + + @Benchmark + fun velocitySystem() { + createVelocitySystem().tick() + } } fun main() { @@ -62,7 +74,7 @@ fun main() { setUp() repeat(400) { -// velocitySystem() + createVelocitySystem() } } } diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt index fa5403469..20ec5b274 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack6Benchmark.kt @@ -47,20 +47,25 @@ class Unpack6Benchmark { } } - //TODO currently no way to avoid delegates -// @Benchmark -// fun unpack6of6CompNoDelegate() { -// systemOf6WithoutDelegate().run { -// forEach { -// comp1 -// comp2 -// comp3 -// comp4 -// comp5 -// comp6 -// } -// } -// } + @Benchmark + fun unpack1of6CompNoDelegate() { + systemOf6WithoutDelegate().forEach { + comp1() + } + } + + // This test gives ridiculous numbers, I think kotlin might just be optimizing some calls away that it can't with a delegate? + @Benchmark + fun unpack6of6CompNoDelegate() { + systemOf6WithoutDelegate().forEach { + comp1() + comp2() + comp3() + comp4() + comp5() + comp6() + } + } } fun main() { @@ -68,6 +73,7 @@ fun main() { setUp() repeat(100) { unpack6of6Comp() +// unpack6of6CompNoDelegate() } } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt index ad1f5aeba..ebba284f5 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt @@ -6,6 +6,7 @@ import com.mineinabyss.geary.components.events.FailedCheck import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.Pipeline import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.ListenerQuery fun GearyModule.listener( diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt index 11f818aec..1864c6daf 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt @@ -1,9 +1,11 @@ package com.mineinabyss.geary.systems +import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.engine.Pipeline import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.query.execOnFinish import kotlin.time.Duration @@ -13,6 +15,23 @@ fun GearyModule.system( return SystemBuilder(query, pipeline) } +class DeferredSystemBuilder( + val systemBuilder: SystemBuilder, + val mapping: CachedQueryRunner.() -> List> +) { + inline fun onFinish(crossinline run: (data: R, entity: GearyEntity) -> Unit): TrackedSystem { + val onTick: CachedQueryRunner.() -> Unit = { + mapping().execOnFinish(run) + } + val system = System( + systemBuilder.query, + onTick as CachedQueryRunner<*>.() -> Unit, + systemBuilder.interval + ) + return systemBuilder.pipeline.addSystem(system) + } +} + class SystemBuilder(val query: T, val pipeline: Pipeline) { var interval: Duration = Duration.ZERO @@ -26,6 +45,14 @@ class SystemBuilder(val query: T, val pipeline: Pipeline) { return pipeline.addSystem(system) } + inline fun defer(crossinline run: T.() -> R): DeferredSystemBuilder { + val onTick: CachedQueryRunner.() -> List> = { + mapWithEntity { run() } + } + val system = DeferredSystemBuilder(this, onTick) + return system + } + fun execOnAll(run: CachedQueryRunner.() -> Unit): TrackedSystem { val system = System( query, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index 8cec087cf..e6851d04b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -10,38 +10,51 @@ import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.EventQueriedEntity import com.mineinabyss.geary.systems.query.QueriedEntity -open class AccessorOperations { +abstract class AccessorOperations { + abstract val cacheAccessors: Boolean + /** Accesses a component, ensuring it is on the entity. */ protected inline fun QueriedEntity.get(): ComponentAccessor { - return NonNullComponentAccessor(this, componentId().withRole(HOLDS_DATA)) + return addAccessor { NonNullComponentAccessor(cacheAccessors, null, this, componentId().withRole(HOLDS_DATA)) } } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ protected inline fun QueriedEntity.getRelation(): ComponentAccessor { - return NonNullComponentAccessor(this, Relation.of().id) + return addAccessor { NonNullComponentAccessor(cacheAccessors, null, this, Relation.of().id) } + } + + inline fun QueriedEntity.addAccessor(create: () -> T): T { + val accessor = create() + accessors.add(accessor) + if (accessor.originalAccessor != null) accessors.remove(accessor.originalAccessor) + return accessor } /** * Accesses a component, allows removing it by setting to null. * As a result, the type is nullable since it may be removed during system runtime. */ - fun ComponentAccessor.removable(): RemovableComponentAccessor { - return RemovableComponentAccessor(queriedEntity, id) - } +// @Deprecated("Removing") +// fun ComponentAccessor.removable(): RemovableComponentAccessor { +// return queriedEntity.addAccessor { RemovableComponentAccessor(this, queriedEntity, id) } +// } /** * Accesses a component or provides a [default] if the entity doesn't have it. * Default gets recalculated on every call to the accessor. */ fun ComponentAccessor.orDefault(default: () -> T): ComponentOrDefaultAccessor { - return ComponentOrDefaultAccessor(id, default) + return queriedEntity.addAccessor { ComponentOrDefaultAccessor(this, queriedEntity, id, default) } } /** Maps an accessor, will recalculate on every call. */ fun > A.map(mapping: (T) -> U): ReadOnlyAccessor { - return if (this is FamilyMatching) - object : ReadOnlyAccessor by MappedAccessor(this, mapping), FamilyMatching by this {} - else MappedAccessor(this, mapping) + return queriedEntity.addAccessor { + when (this) { + is FamilyMatching -> object : ReadOnlyAccessor by MappedAccessor(this, mapping), FamilyMatching by this {} + else -> MappedAccessor(this, mapping) + } + } } /** Accesses a component or `null` if the entity doesn't have it. */ @@ -62,12 +75,12 @@ open class AccessorOperations { * - Note: nullability rules are still upheld with [Any]. */ protected inline fun QueriedEntity.getRelations(): RelationsAccessor { - return RelationsAccessor(this, componentIdWithNullable(), componentIdWithNullable()) + return addAccessor { RelationsAccessor(null, this, componentIdWithNullable(), componentIdWithNullable()) } } /** @see getRelations */ protected inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { - return RelationsWithDataAccessor(this, componentIdWithNullable(), componentIdWithNullable()) + return addAccessor { RelationsWithDataAccessor(null, this, componentIdWithNullable(), componentIdWithNullable()) } } protected operator fun QueriedEntity.invoke(init: MutableFamily.Selector.And.() -> Unit) { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt index 312afac6e..2deed5d8f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt @@ -1,8 +1,18 @@ package com.mineinabyss.geary.systems.accessors +import com.mineinabyss.geary.systems.query.QueriedEntity import com.mineinabyss.geary.systems.query.Query import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty -typealias ReadOnlyAccessor = ReadOnlyProperty -typealias ReadWriteAccessor = ReadWriteProperty +interface Accessor { + val originalAccessor: Accessor? + val queriedEntity: QueriedEntity +} +interface ReadOnlyAccessor : Accessor, ReadOnlyProperty { + +} + +interface ReadWriteAccessor : ReadOnlyAccessor, ReadWriteProperty { + +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt index f6dee8d3a..1aa4d887f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/FamilyMatching.kt @@ -8,6 +8,5 @@ import com.mineinabyss.geary.systems.query.QueriedEntity * (ex a component accessor needs the component present on the entity.) */ interface FamilyMatching { - val queriedEntity: QueriedEntity val family: Family.Selector? } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 395250c5a..5419cd653 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -1,58 +1,67 @@ package com.mineinabyss.geary.systems.accessors.type -import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.systems.accessors.AccessorOperations +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadWriteAccessor import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) abstract class ComponentAccessor( - override val queriedEntity: QueriedEntity, + val cacheArchetypeInfo: Boolean, + override val originalAccessor: Accessor?, + final override val queriedEntity: QueriedEntity, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { override val family = family { hasSet(id) } protected var cachedIndex = -1 protected var cachedDataArray: MutableList = mutableListOf() - protected var cachedArchetype: Archetype? = null - abstract operator fun get(thisRef: AccessorOperations): T + @PublishedApi + internal fun updateCache(archetype: Archetype) { + cachedIndex = archetype.indexOf(id) + if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList + } - internal inline fun get(thisRef: AccessorOperations, beforeRead: () -> Unit): T { - val archetype = queriedEntity.archetype - if (archetype !== cachedArchetype) { - cachedArchetype = archetype - cachedIndex = archetype.indexOf(id) - if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList - } + abstract fun get(thisRef: Query): T + + internal inline fun get(query: Query, beforeRead: () -> Unit): T { beforeRead() - return cachedDataArray[queriedEntity.row] + if (!cacheArchetypeInfo) { + updateCache(queriedEntity.archetype) + return cachedDataArray[queriedEntity.row] + } + return cachedDataArray[query.row] } - abstract operator fun set(thisRef: AccessorOperations, value: T) + abstract fun set(query: Query, value: T) - internal inline fun set(thisRef: AccessorOperations, value: T, beforeWrite: () -> Unit) { - val archetype = queriedEntity.archetype - if (archetype !== cachedArchetype) { - cachedArchetype = archetype - cachedIndex = archetype.indexOf(id) - if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList - } + internal inline fun set(query: Query, value: T, beforeWrite: () -> Unit) { beforeWrite() - cachedDataArray[queriedEntity.row] = value + if (!cacheArchetypeInfo) { + updateCache(query.archetype) + if (value == null) { + queriedEntity.unsafeEntity.remove(id) + return + } + if (cachedIndex == -1) queriedEntity.unsafeEntity.set(value, id) + else cachedDataArray[queriedEntity.row] = value + return + } + cachedDataArray[query.row] = value } - final override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): T { + final override fun getValue(thisRef: Query, property: KProperty<*>): T { return get(thisRef) } - final override fun setValue(thisRef: AccessorOperations, property: KProperty<*>, value: T) { + final override fun setValue(thisRef: Query, property: KProperty<*>, value: T) { return set(thisRef, value) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt index bd6b9483f..56ec7f6c1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentOrDefaultAccessor.kt @@ -3,30 +3,30 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import com.mineinabyss.geary.systems.query.QueriedEntity import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) class ComponentOrDefaultAccessor( + override val originalAccessor: Accessor?, + override val queriedEntity: QueriedEntity, val id: ComponentId, val default: () -> T, ) : ReadOnlyAccessor { private var cachedIndex = -1 private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): T { - TODO("Not yet implemented") + override fun getValue(thisRef: Query, property: KProperty<*>): T { + val archetype = queriedEntity.archetype + if (archetype !== cachedArchetype) { + cachedArchetype = archetype + cachedIndex = archetype.indexOf(id) + } + if (cachedIndex == -1) return default() + return archetype.componentData[cachedIndex][thisRef.row] as T } - -// override fun getValue(thisRef: Pointer, property: KProperty<*>): T { -// val archetype = thisRef.archetype -// if (archetype !== cachedArchetype) { -// cachedArchetype = archetype -// cachedIndex = archetype.indexOf(id) -// } -// if (cachedIndex == -1) return default() -// return archetype.componentData[cachedIndex][thisRef.row] as T -// } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt index a753a599b..9b903063b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt @@ -2,11 +2,17 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor +import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty -class MappedAccessor(val original: ReadOnlyAccessor, val mapping: (T) -> U) : ReadOnlyAccessor { - override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): U { - val value = original.getValue(thisRef, property) +class MappedAccessor( + override val originalAccessor: ReadOnlyAccessor, + val mapping: (T) -> U, +) : ReadOnlyAccessor { + override val queriedEntity: QueriedEntity = originalAccessor.queriedEntity + override fun getValue(thisRef: Query, property: KProperty<*>): U { + val value = originalAccessor.getValue(thisRef, property) return mapping(value) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt index a647bfa97..bca6a8dba 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/NonNullComponentAccessor.kt @@ -1,24 +1,20 @@ package com.mineinabyss.geary.systems.accessors.type import com.mineinabyss.geary.datatypes.ComponentId -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.systems.accessors.AccessorOperations +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query -@OptIn(UnsafeAccessors::class) class NonNullComponentAccessor( + cacheArchetypeInfo: Boolean, + originalAccessor: Accessor?, entity: QueriedEntity, id: ComponentId, -) : ComponentAccessor(entity, id) { - override operator fun get(thisRef: AccessorOperations): T = +) : ComponentAccessor(cacheArchetypeInfo, originalAccessor, entity, id) { + override fun get(thisRef: Query): T = get(thisRef, beforeRead = {}) - override operator fun set(thisRef: AccessorOperations, value: T) { - set(thisRef, value, beforeWrite = { - if (cachedIndex == -1) { - queriedEntity.entity.set(value, id) - return - } - }) + override fun set(query: Query, value: T) { + set(query, value, beforeWrite = {}) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt index 4b5ea91af..7d6bce6b0 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsAccessor.kt @@ -7,13 +7,16 @@ import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty class RelationsAccessor( + override val originalAccessor: Accessor?, override val queriedEntity: QueriedEntity, val kind: ComponentId, val target: EntityId, @@ -24,7 +27,7 @@ class RelationsAccessor( private var cachedArchetype: Archetype? = null @OptIn(UnsafeAccessors::class) - override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List { + override fun getValue(thisRef: Query, property: KProperty<*>): List { val archetype = queriedEntity.archetype if (archetype != cachedArchetype) { cachedArchetype = archetype diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt index bf203658e..a4d50db77 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RelationsWithDataAccessor.kt @@ -4,19 +4,16 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation -import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.systems.accessors.AccessorOperations -import com.mineinabyss.geary.systems.accessors.FamilyMatching -import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor -import com.mineinabyss.geary.systems.accessors.RelationWithData +import com.mineinabyss.geary.systems.accessors.* import com.mineinabyss.geary.systems.query.QueriedEntity import com.mineinabyss.geary.systems.query.Query import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) class RelationsWithDataAccessor( + override val originalAccessor: Accessor?, override val queriedEntity: QueriedEntity, val kind: ComponentId, val target: EntityId, @@ -26,13 +23,18 @@ class RelationsWithDataAccessor( private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null - override fun getValue(thisRef: AccessorOperations, property: KProperty<*>): List> { + override fun getValue(thisRef: Query, property: KProperty<*>): List> { val archetype = queriedEntity.archetype if (archetype != cachedArchetype) { cachedArchetype = archetype cachedRelations = archetype.getRelations(kind, target) } - return archetype.readRelationDataFor(queriedEntity.row, kind, target, cachedRelations) as List> + return archetype.readRelationDataFor( + queriedEntity.row, + kind, + target, + cachedRelations + ) as List> } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt deleted file mode 100644 index ac9598d01..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/RemovableComponentAccessor.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.mineinabyss.geary.systems.accessors.type - -import com.mineinabyss.geary.datatypes.ComponentId -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.modules.archetypes -import com.mineinabyss.geary.systems.accessors.AccessorOperations -import com.mineinabyss.geary.systems.query.QueriedEntity - -class RemovableComponentAccessor( - entity: QueriedEntity, - id: ComponentId, -) : ComponentAccessor(entity, id) { - override fun get(thisRef: AccessorOperations): T? = - get(thisRef, beforeRead = { - if (cachedIndex == -1) return null - }) - - @OptIn(UnsafeAccessors::class) - override fun set(thisRef: AccessorOperations, value: T?) = - set(thisRef, value, beforeWrite = { - if (cachedIndex == -1) { - if (value == null) return - else { - queriedEntity.entity.set(value, id) - queriedEntity.delegateTo(archetypes.records[queriedEntity.entity]) - } - return - } - if (value == null) { - queriedEntity.entity.remove(id) - queriedEntity.delegateTo(archetypes.records[queriedEntity.entity]) - return - } - }) -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index 8f396a3fc..940478161 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -1,12 +1,15 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.datatypes.RecordPointer +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.helpers.fastForEachWithIndex +import com.mineinabyss.geary.helpers.fastForEach +import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() val family = query.buildFamily() + val accessors = query.accessors.toTypedArray().filterIsInstance>() inline fun toList(crossinline map: T.() -> R): List { val list = mutableListOf() @@ -23,21 +26,48 @@ class CachedQueryRunner internal constructor(val query: T) { val matched = matchedArchetypes var n = 0 val size = matched.size // Get size ahead of time to avoid rerunning on entities that end up in new archetypes + val accessors = accessors while (n < size) { val archetype = matched[n] archetype.isIterating = true val upTo = archetype.size - val target = query + var row = 0 + query.originalArchetype = archetype // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration - target.originalArchetype = archetype - - for (entityIndex in 0 until upTo) { - target.delegated = false - target.originalRow = entityIndex + accessors.fastForEach { it.updateCache(archetype) } + while (row < upTo) { + query.originalRow = row run(query) + row++ } archetype.isIterating = false n++ } } + + inline fun map(crossinline run: T.() -> R): List { + val deferred = mutableListOf() + forEach { + deferred.add(run()) + } + return deferred + } + + data class Deferred( + val data: R, + val entity: GearyEntity + ) + + @OptIn(UnsafeAccessors::class) + inline fun mapWithEntity(crossinline run: T.() -> R): List> { + val deferred = mutableListOf>() + forEach { + deferred.add(Deferred(run(), archetype.getEntity(row))) + } + return deferred + } +} + +inline fun List>.execOnFinish(run: (data: R, entity: GearyEntity) -> Unit) { + fastForEach { run(it.data, it.entity) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index 5a1ce5df3..072fa407e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -1,16 +1,15 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.components.events.ExtendedEntity -import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.helpers.toGeary +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching -import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import kotlin.reflect.KProperty -abstract class ListenerQuery : QueriedEntity() { +abstract class ListenerQuery : Query() { + override val cacheAccessors: Boolean = false val event: EventQueriedEntity = EventQueriedEntity() - val source: QueriedEntity = QueriedEntity() + val source: QueriedEntity = QueriedEntity(false) data class Families( val event: Family.Selector.And, @@ -24,19 +23,6 @@ abstract class ListenerQuery : QueriedEntity() { source = source.buildFamily(), ) - protected open fun ensure() {} - - internal fun initialize() = ensure() - - /** Automatically matches families for any accessor that's supposed to match a family. */ - operator fun T.provideDelegate( - thisRef: Any, - prop: KProperty<*> - ): T { - family?.let { queriedEntity.props[prop] = it } - return this - } - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ protected fun onAdd(vararg props: KProperty<*>) { @@ -47,19 +33,25 @@ abstract class ListenerQuery : QueriedEntity() { } + @OptIn(UnsafeAccessors::class) + val QueriedEntity.entity get() = unsafeEntity - /** Fires when an entity has a component of type [T] set or updated. */ protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { - val names = props.map { it.name }.toSet() + anySet(*props.mapNotNull { this@ListenerQuery.props[it.name] }.toTypedArray()) + } + + /** Fires when an entity has a component of type [T] set or updated. */ + protected fun EventQueriedEntity.anySet(vararg props: Accessor) { invoke { - this@ListenerQuery.props.filterKeys { prop -> - prop.name in names - }.values.flatMap { - it.components - }.forEach { component -> - onSet(component) - // TODO do we error here if not, this isn't really typesafe? - } + this@ListenerQuery.accessors.intersect(props.toSet()) + .asSequence() + .filterIsInstance() + .mapNotNull { it.family } + .flatMap { it.components } + .forEach { component -> + onSet(component) + // TODO do we error here if not, this isn't really typesafe? + } } } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index f3ac26f9f..fa10798aa 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -7,18 +7,28 @@ import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations -import kotlin.reflect.KProperty +import com.mineinabyss.geary.systems.accessors.FamilyMatching + +open class EventQueriedEntity : QueriedEntity(cacheAccessors = false) +open class QueriedEntity( + override val cacheAccessors: Boolean +) : AccessorOperations() { -open class EventQueriedEntity: QueriedEntity() -open class QueriedEntity: AccessorOperations() { internal val extraFamilies: MutableList = mutableListOf() - internal val props: MutableMap, Family.Selector> = mutableMapOf() + + internal val props: MutableMap = mutableMapOf() + + @PublishedApi + internal val accessors: MutableSet = mutableSetOf() fun buildFamily(): Family.Selector.And = family { - for (family in props.values.union(extraFamilies)) { - add(family) - } + accessors + .filterIsInstance() + .mapNotNull { it.family } + .union(extraFamilies) + .forEach(::add) } @PublishedApi @@ -42,7 +52,7 @@ open class QueriedEntity: AccessorOperations() { } @UnsafeAccessors - val entity: Entity + val unsafeEntity: Entity get() { val entity = archetype.getEntity(row) if (!delegated) { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index 509e89e07..dea15d1b4 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -1,21 +1,28 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.systems.accessors.FamilyMatching +import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.systems.accessors.Accessor +import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor import kotlin.reflect.KProperty -abstract class Query : QueriedEntity() { - +abstract class Query : QueriedEntity(cacheAccessors = true) { //TODO duplicate with EventQuery /** Automatically matches families for any accessor that's supposed to match a family. */ - operator fun T.provideDelegate( + operator fun T.provideDelegate( thisRef: Any, prop: KProperty<*> ): T { - family?.let { queriedEntity.props[prop] = it } + queriedEntity.props[prop.name] = this return this } protected open fun ensure() {} - internal fun initialize() = ensure() + internal fun initialize() { + ensure() + } + + inline operator fun ComponentAccessor.invoke(): T = get(this@Query) + inline fun ComponentAccessor.set(value: T) = set(this@Query, value) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt index 19d918058..d91ea3d3a 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt @@ -23,6 +23,7 @@ class SourceTargetEventTest : GearyTest() { @Test fun interactions() { + resetEngine() interactionListener() val source = entity { set(Strength(10)) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt index 59ca95e62..e7d4941ec 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/SimpleQueryTest.kt @@ -17,18 +17,17 @@ class SimpleQueryTest : GearyTest() { val query = geary.queryManager.trackQuery(MyQuery()) repeat(10) { entity { - set(1) + set(it) } entity { set("Not this!") } } - var count = 0 + val nums = mutableListOf() query.forEach { - int shouldBe 1 - count++ + nums.add(int) } - count shouldBe 10 + nums.sorted() shouldBe (0..9).toList() } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index cfcd5d98d..c02a6b692 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -1,6 +1,5 @@ package com.mineinabyss.geary.systems -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.HOLDS_DATA import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.entity @@ -17,12 +16,11 @@ class FamilyMatchingTest : GearyTest() { val stringId = componentId() or HOLDS_DATA val intId = componentId() - @OptIn(UnsafeAccessors::class) val system = geary.system(object : Query() { val string by get() override fun ensure() = this { has() } - }).exec { - string shouldBe entity.get() + }).defer { string }.onFinish { data, entity -> + data shouldBe entity.get() entity.has() shouldBe true } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt similarity index 74% rename from geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt rename to geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt index 1706e8f43..dd82a5adc 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/UnsafeQueryAccessTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt @@ -6,11 +6,13 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.cachedQuery +import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe import kotlin.test.Test -class UnsafeQueryAccessTests : GearyTest() { +class ListenerLiveEntityModificationTests : GearyTest() { private fun registerQuery() = geary.cachedQuery(object : Query() { var data by get() }) @@ -32,16 +34,14 @@ class UnsafeQueryAccessTests : GearyTest() { count shouldBe 1 } - @OptIn(UnsafeAccessors::class) @Test - fun `should allow data modify when entity changed archetype by setting`() { + fun `should allow data modify when entity archetype changed by SET`() { resetEngine() - entity { - set(Comp1(1)) - } - var count = 0 - registerQuery().forEach { + + geary.listener(object : ListenerQuery() { + var data by get() + }).exec { data shouldBe Comp1(1) data = Comp1(10) entity.set("Other comp") @@ -49,19 +49,22 @@ class UnsafeQueryAccessTests : GearyTest() { data shouldBe Comp1(10) count++ } + + entity { + set(Comp1(1)) + }.callEvent() count shouldBe 1 } @OptIn(UnsafeAccessors::class) @Test - fun `should allow data modify when entity changed archetype by removing`() { + fun `should allow data modify when entity archetype changed by REMOVE`() { resetEngine() - entity { - set(Comp1(1)) - } - var count = 0 - registerQuery().forEach { + + geary.listener(object : ListenerQuery() { + var data by get() + }).exec { data shouldBe Comp1(1) entity.remove() data = Comp1(10) @@ -70,6 +73,9 @@ class UnsafeQueryAccessTests : GearyTest() { data shouldBe Comp1(10) count++ } + entity { + set(Comp1(1)) + }.callEvent() count shouldBe 1 } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt deleted file mode 100644 index 23c2de8a3..000000000 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/RemovableAccessorTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.mineinabyss.geary.systems.accessors - -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.helpers.Comp1 -import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.cachedQuery -import com.mineinabyss.geary.systems.query.Query -import io.kotest.matchers.shouldBe -import kotlin.test.Test - -class RemovableAccessorTest : GearyTest() { - private fun createRemovableQuery() = geary.cachedQuery(object : Query() { - var data by get().removable() - }) - - - @OptIn(UnsafeAccessors::class) - @Test - fun `should allow removing component via removable accessor`() { - resetEngine() - entity { - set(Comp1(1)) - } - var count = 0 - - createRemovableQuery().forEach { - data shouldBe Comp1(1) - data = null - data shouldBe null - entity.has() shouldBe false - count++ - } - count shouldBe 1 - } -} From 9cf2748595ce6edd7439d5db30f07636693836dd Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sun, 25 Feb 2024 17:36:20 -0500 Subject: [PATCH 10/14] Some cleanup for systems - Add syntax for declaring repeating systems --- .../systems/CopyToInstancesSystem.kt | 2 +- .../systems/ParseChildOnPrefab.kt | 3 +- .../systems/ParseRelationOnPrefab.kt | 2 +- .../systems/ParseRelationWithDataSystem.kt | 2 +- .../prefabs/systems/InheritPrefabsOnLoad.kt | 2 +- .../systems/TrackPrefabsByKeySystem.kt | 2 +- .../geary/uuid/systems/TrackUuidOnAdd.kt | 2 +- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 2 +- .../benchmarks/VelocitySystemBenchmark.kt | 2 +- .../geary/benchmarks/events/EventCalls.kt | 2 +- .../geary/benchmarks/unpacking/Systems.kt | 2 +- .../benchmarks/unpacking/Unpack1Benchmark.kt | 3 - .../com/mineinabyss/geary/engine/Pipeline.kt | 9 +-- .../mineinabyss/geary/engine/PipelineImpl.kt | 31 ++++----- .../engine/archetypes/ArchetypeEngine.kt | 8 ++- .../engine/archetypes/ArchetypeEventRunner.kt | 22 +++---- .../geary/systems/CachedQueryBuilder.kt | 12 ---- .../mineinabyss/geary/systems/GearyAliases.kt | 2 +- .../com/mineinabyss/geary/systems/System.kt | 8 +-- .../geary/systems/SystemBuilder.kt | 64 ------------------- .../geary/systems/TrackedSystem.kt | 7 +- .../systems/builders/DeferredSystemBuilder.kt | 21 ++++++ .../geary/systems/builders/GlobalFunctions.kt | 26 ++++++++ .../systems/{ => builders}/ListenerBuilder.kt | 12 +--- .../geary/systems/builders/SystemBuilder.kt | 37 +++++++++++ .../geary/systems/query/QueriedEntity.kt | 5 -- .../geary/events/CheckingListenerTest.kt | 2 +- .../geary/events/ComponentAddEventTest.kt | 2 +- .../events/SimpleComponentAddListenerTest.kt | 2 +- .../geary/events/SimpleEventTest.kt | 2 +- .../geary/events/SourceTargetEventTest.kt | 2 +- .../geary/systems/FamilyMatchingTest.kt | 1 + .../geary/systems/QueryManagerTest.kt | 1 + .../systems/RelationMatchingSystemTest.kt | 1 + .../ListenerLiveEntityModificationTests.kt | 4 +- 35 files changed, 149 insertions(+), 158 deletions(-) delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/DeferredSystemBuilder.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt rename geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/{ => builders}/ListenerBuilder.kt (78%) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/SystemBuilder.kt diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt index 276c94500..47c3fc9b8 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/CopyToInstancesSystem.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery @OptIn(UnsafeAccessors::class) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt index f349682cc..e290805fe 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseChildOnPrefab.kt @@ -1,6 +1,5 @@ package com.mineinabyss.geary.prefabs.configuration.systems -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.helpers.addParent @@ -9,7 +8,7 @@ import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.ChildOnPrefab import com.mineinabyss.geary.prefabs.configuration.components.ChildrenOnPrefab import com.mineinabyss.geary.prefabs.configuration.components.Prefab -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt index 9d3ba6675..05b732542 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt @@ -2,7 +2,7 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt index 2bed40bc2..0f5d108f5 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationWithDataSystem.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.accessors.RelationWithData -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery @OptIn(UnsafeAccessors::class) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt index c6d887335..12243d9b0 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/InheritPrefabsOnLoad.kt @@ -4,7 +4,7 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.events.PrefabLoaded import com.mineinabyss.geary.prefabs.helpers.inheritPrefabs -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery @OptIn(UnsafeAccessors::class) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt index d3e41349f..c2792092b 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/systems/TrackPrefabsByKeySystem.kt @@ -5,7 +5,7 @@ import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.PrefabKey import com.mineinabyss.geary.prefabs.prefabs -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery @OptIn(UnsafeAccessors::class) diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt index de54bbc9c..23c0de06f 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt @@ -4,7 +4,7 @@ import com.benasher44.uuid.Uuid import com.benasher44.uuid.uuid4 import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash import com.mineinabyss.geary.uuid.uuid2Geary diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index be48bb81a..aa1813319 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid import com.mineinabyss.geary.components.events.EntityRemoved import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.uuid2Geary diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt index 442f8450a..e9905f8a3 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/VelocitySystemBenchmark.kt @@ -6,7 +6,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.query.Query -import com.mineinabyss.geary.systems.system +import com.mineinabyss.geary.systems.builders.system import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt index dfbbf8385..ae3fd0e1c 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/events/EventCalls.kt @@ -5,7 +5,7 @@ import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.idofront.di.DI import org.openjdk.jmh.annotations.* diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt index 3e2a02b1c..423d15ece 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Systems.kt @@ -2,7 +2,7 @@ package com.mineinabyss.geary.benchmarks.unpacking import com.mineinabyss.geary.benchmarks.helpers.* import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.cachedQuery +import com.mineinabyss.geary.systems.builders.cachedQuery import com.mineinabyss.geary.systems.query.GearyQuery class Query1 : GearyQuery() { diff --git a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt index 1422ab1fa..63b00bf44 100644 --- a/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt +++ b/geary-benchmarks/src/main/kotlin/com/mineinabyss/geary/benchmarks/unpacking/Unpack1Benchmark.kt @@ -5,9 +5,6 @@ import com.mineinabyss.geary.benchmarks.helpers.tenMil import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.cachedQuery -import com.mineinabyss.geary.systems.query.GearyQuery -import com.mineinabyss.geary.systems.system import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Setup diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt index 3313812b3..5fee33380 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Pipeline.kt @@ -4,19 +4,20 @@ import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.System import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.query.Query interface Pipeline { fun runOnOrAfter(phase: GearyPhase, block: () -> Unit) - fun interceptSystemAddition(run: (System) -> System?) + fun onSystemAdd(run: (System<*>) -> Unit) fun runStartupTasks() /** Adds a [system] to the engine, which will be ticked appropriately by the engine. */ - fun addSystem(system: System): TrackedSystem + fun addSystem(system: System): TrackedSystem<*> - fun addSystems(vararg systems: System) + fun addSystems(vararg systems: System<*>) fun addListener(listener: Listener<*>): Listener<*> /** Gets all registered systems in the order they should be executed during an engine tick. */ - fun getRepeatingInExecutionOrder(): Iterable + fun getRepeatingInExecutionOrder(): Iterable> } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt index bf3fedd10..9e77cef5a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/PipelineImpl.kt @@ -5,15 +5,13 @@ import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.System import com.mineinabyss.geary.systems.TrackedSystem -import com.mineinabyss.geary.systems.cachedQuery -import com.mineinabyss.geary.systems.query.CachedQueryRunner -import kotlin.time.Duration +import com.mineinabyss.geary.systems.query.Query class PipelineImpl : Pipeline { private val queryManager get() = geary.queryManager - private val onSystemRegister = mutableListOf<(System) -> System?>() - private val registeredSystems: MutableSet = mutableSetOf() + private val onSystemAdd = mutableListOf<(System<*>) -> Unit>() + private val repeatingSystems: MutableSet> = mutableSetOf() private val registeredListeners: MutableSet> = mutableSetOf() private val scheduled = Array(GearyPhase.entries.size) { mutableListOf<() -> Unit>() } @@ -24,8 +22,8 @@ class PipelineImpl : Pipeline { else scheduled[phase.ordinal].add(block) } - override fun interceptSystemAddition(run: (System) -> System?) { - onSystemRegister.add(run) + override fun onSystemAdd(run: (System<*>) -> Unit) { + onSystemAdd.add(run) } override fun runStartupTasks() { @@ -34,22 +32,17 @@ class PipelineImpl : Pipeline { } } - override fun addSystem(system: System): TrackedSystem { -// val resultSystem = onSystemRegister.fold(system) { acc, func -> func(acc) ?: return } - // Track systems right at startup since they are likely going to tick very soon anyway, and we don't care about - // any hiccups at that point. + override fun addSystem(system: System): TrackedSystem<*> { + onSystemAdd.forEach { it(system) } val runner = queryManager.trackQuery(system.query) val tracked = TrackedSystem(system, runner) - if (system.interval != Duration.ZERO) { - registeredSystems.add(tracked) + if (system.interval != null) { + repeatingSystems.add(tracked) } return TrackedSystem(system, runner) -// when (resultSystem) { -// else -> resultSystem.onStart() -// } } - override fun addSystems(vararg systems: System) { + override fun addSystems(vararg systems: System<*>) { systems.forEach { addSystem(it) } } @@ -60,7 +53,7 @@ class PipelineImpl : Pipeline { return listener } - override fun getRepeatingInExecutionOrder(): Iterable { - return registeredSystems + override fun getRepeatingInExecutionOrder(): Iterable> { + return repeatingSystems } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt index 596ba0506..bc6696278 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEngine.kt @@ -4,6 +4,7 @@ import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.engine.* import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.query.Query import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.time.Duration @@ -24,7 +25,7 @@ open class ArchetypeEngine(override val tickDuration: Duration) : TickingEngine( (CoroutineScope(Dispatchers.Default) + CoroutineName("Geary Engine")).coroutineContext /** Describes how to individually tick each system */ - protected open fun TrackedSystem.runSystem() { + protected open fun TrackedSystem.runSystem() { system.onTick(runner) } @@ -41,7 +42,10 @@ open class ArchetypeEngine(override val tickDuration: Duration) : TickingEngine( override fun tick(currentTick: Long) { // Create a job but don't start it pipeline.getRepeatingInExecutionOrder() - .filter { currentTick % (it.system.interval / tickDuration).toInt().coerceAtLeast(1) == 0L } + .filter { + it.system.interval != null + && (currentTick % (it.system.interval / tickDuration).toInt().coerceAtLeast(1) == 0L) + } .also { logger.v("Ticking engine with systems $it") } .forEach { system -> runCatching { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt index 9c3b71a1f..6d2a9f41e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ArchetypeEventRunner.kt @@ -7,6 +7,7 @@ import com.mineinabyss.geary.engine.EventRunner import com.mineinabyss.geary.helpers.fastForEach import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.systems.Listener +import com.mineinabyss.geary.systems.query.QueriedEntity class ArchetypeEventRunner : EventRunner { private val records: TypeMap get() = archetypes.records @@ -20,20 +21,17 @@ class ArchetypeEventRunner : EventRunner { val targetArc = target.archetype val sourceArc = source?.archetype + fun QueriedEntity.reset(record: Record) { + originalArchetype = record.archetype + originalRow = record.row + delegated = false + } + fun callListener(listener: Listener<*>) { val query = listener.query - //TODO function extract - query.event.delegated = false - query.event.originalArchetype = event.archetype - query.event.originalRow = event.row - query.delegated = false - query.originalArchetype = target.archetype - query.originalRow = target.row - if (source != null) { - query.source.delegated = false - query.source.originalArchetype = source.archetype - query.source.originalRow = source.row - } + query.event.reset(event) + query.reset(target) + source?.let { query.source.reset(it) } listener.run() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt deleted file mode 100644 index 1b83d4cee..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/CachedQueryBuilder.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.mineinabyss.geary.systems - -import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.systems.query.CachedQueryRunner -import com.mineinabyss.geary.systems.query.Query - - -fun GearyModule.cachedQuery( - query: T, -): CachedQueryRunner { - return queryManager.trackQuery(query) -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt index 385d4029d..41ddb4820 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/GearyAliases.kt @@ -1,4 +1,4 @@ package com.mineinabyss.geary.systems typealias GearyListener = Listener -typealias GearySystem = System +typealias GearySystem = System diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt index c321eb89f..763dad152 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/System.kt @@ -4,8 +4,8 @@ import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.Query import kotlin.time.Duration -class System @PublishedApi internal constructor( - val query: Query, - val onTick: CachedQueryRunner<*>.() -> Unit, - val interval: Duration, +class System @PublishedApi internal constructor( + val query: T, + val onTick: CachedQueryRunner.() -> Unit, + val interval: Duration?, ) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt deleted file mode 100644 index 1864c6daf..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/SystemBuilder.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.mineinabyss.geary.systems - -import com.mineinabyss.geary.datatypes.GearyEntity -import com.mineinabyss.geary.engine.Pipeline -import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.systems.query.CachedQueryRunner -import com.mineinabyss.geary.systems.query.Query -import com.mineinabyss.geary.systems.query.execOnFinish -import kotlin.time.Duration - - -fun GearyModule.system( - query: T -): SystemBuilder { - return SystemBuilder(query, pipeline) -} - -class DeferredSystemBuilder( - val systemBuilder: SystemBuilder, - val mapping: CachedQueryRunner.() -> List> -) { - inline fun onFinish(crossinline run: (data: R, entity: GearyEntity) -> Unit): TrackedSystem { - val onTick: CachedQueryRunner.() -> Unit = { - mapping().execOnFinish(run) - } - val system = System( - systemBuilder.query, - onTick as CachedQueryRunner<*>.() -> Unit, - systemBuilder.interval - ) - return systemBuilder.pipeline.addSystem(system) - } -} - -class SystemBuilder(val query: T, val pipeline: Pipeline) { - var interval: Duration = Duration.ZERO - - inline fun exec(crossinline run: T.() -> Unit): TrackedSystem { - val onTick: CachedQueryRunner.() -> Unit = { forEach(run) } - val system = System( - query, - onTick as CachedQueryRunner<*>.() -> Unit, - interval - ) - return pipeline.addSystem(system) - } - - inline fun defer(crossinline run: T.() -> R): DeferredSystemBuilder { - val onTick: CachedQueryRunner.() -> List> = { - mapWithEntity { run() } - } - val system = DeferredSystemBuilder(this, onTick) - return system - } - - fun execOnAll(run: CachedQueryRunner.() -> Unit): TrackedSystem { - val system = System( - query, - run as CachedQueryRunner<*>.() -> Unit, - interval - ) - return pipeline.addSystem(system) - } -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt index 88989c0bd..d289191a8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/TrackedSystem.kt @@ -1,10 +1,11 @@ package com.mineinabyss.geary.systems import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query -class TrackedSystem( - val system: System, - val runner: CachedQueryRunner<*> +class TrackedSystem( + val system: System, + val runner: CachedQueryRunner ) { fun tick() { system.onTick(runner) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/DeferredSystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/DeferredSystemBuilder.kt new file mode 100644 index 000000000..7a108e126 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/DeferredSystemBuilder.kt @@ -0,0 +1,21 @@ +package com.mineinabyss.geary.systems.builders + +import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.systems.System +import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.geary.systems.query.execOnFinish + +class DeferredSystemBuilder( + val systemBuilder: SystemBuilder, + val mapping: CachedQueryRunner.() -> List> +) { + inline fun onFinish(crossinline run: (data: R, entity: GearyEntity) -> Unit): TrackedSystem<*> { + val onTick: CachedQueryRunner.() -> Unit = { + mapping().execOnFinish(run) + } + val system = System(systemBuilder.query, onTick, systemBuilder.interval) + return systemBuilder.pipeline.addSystem(system) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt new file mode 100644 index 000000000..d7f31ed10 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt @@ -0,0 +1,26 @@ +package com.mineinabyss.geary.systems.builders + +import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.ListenerQuery +import com.mineinabyss.geary.systems.query.Query + + +fun GearyModule.cachedQuery( + query: T, +): CachedQueryRunner { + return queryManager.trackQuery(query) +} + +fun GearyModule.listener( + query: T +): ListenerBuilder { + return ListenerBuilder(query, pipeline) +} + + +fun GearyModule.system( + query: T +): SystemBuilder { + return SystemBuilder(query, pipeline) +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/ListenerBuilder.kt similarity index 78% rename from geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt rename to geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/ListenerBuilder.kt index ebba284f5..760d14eec 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/ListenerBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/ListenerBuilder.kt @@ -1,20 +1,13 @@ -package com.mineinabyss.geary.systems +package com.mineinabyss.geary.systems.builders -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.components.RequestCheck import com.mineinabyss.geary.components.events.FailedCheck import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.Pipeline import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.Listener import com.mineinabyss.geary.systems.query.ListenerQuery -fun GearyModule.listener( - query: T -): ListenerBuilder { - return ListenerBuilder(query, pipeline) -} - class ListenerBuilder( val query: T, val pipeline: Pipeline, @@ -29,7 +22,6 @@ class ListenerBuilder( return pipeline.addListener(listener) } - @OptIn(UnsafeAccessors::class) fun check(check: T.() -> Boolean): Listener<*> { query.initialize() val families = query.buildFamilies() diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/SystemBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/SystemBuilder.kt new file mode 100644 index 000000000..df1e18dd4 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/SystemBuilder.kt @@ -0,0 +1,37 @@ +package com.mineinabyss.geary.systems.builders + +import com.mineinabyss.geary.engine.Pipeline +import com.mineinabyss.geary.systems.System +import com.mineinabyss.geary.systems.TrackedSystem +import com.mineinabyss.geary.systems.query.CachedQueryRunner +import com.mineinabyss.geary.systems.query.Query +import kotlin.time.Duration + +class SystemBuilder( + val query: T, + val pipeline: Pipeline, + val interval: Duration? = null +) { + fun every(interval: Duration): SystemBuilder { + return SystemBuilder(query, pipeline, interval) + } + + inline fun exec(crossinline run: T.() -> Unit): TrackedSystem<*> { + val onTick: CachedQueryRunner.() -> Unit = { forEach(run) } + val system = System(query, onTick, interval) + return pipeline.addSystem(system) + } + + inline fun defer(crossinline run: T.() -> R): DeferredSystemBuilder { + val onTick: CachedQueryRunner.() -> List> = { + mapWithEntity { run() } + } + val system = DeferredSystemBuilder(this, onTick) + return system + } + + fun execOnAll(run: CachedQueryRunner.() -> Unit): TrackedSystem<*> { + val system = System(query, run, interval) + return pipeline.addSystem(system) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index fa10798aa..a417c1a1a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -46,11 +46,6 @@ open class QueriedEntity( @PublishedApi internal var delegated = false - internal fun delegateTo(record: Record) { - delegated = true - delegate = record - } - @UnsafeAccessors val unsafeEntity: Entity get() { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt index 37d9afd33..3e42247fa 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/CheckingListenerTest.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.events import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt index 8278deeb9..5c0db3ad4 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/ComponentAddEventTest.kt @@ -4,7 +4,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt index 3da4027c2..f9645237e 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleComponentAddListenerTest.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.events import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt index e2fdceed9..a9ccbc9cd 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SimpleEventTest.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.events import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import kotlin.test.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt index d91ea3d3a..f25918c27 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/events/SourceTargetEventTest.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.events import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index c02a6b692..d7b21c806 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -6,6 +6,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.builders.system import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainAll diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt index 6895682fe..a188d78d1 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/QueryManagerTest.kt @@ -5,6 +5,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt index 42d38cc26..7b13196ba 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/RelationMatchingSystemTest.kt @@ -7,6 +7,7 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.builders.system import com.mineinabyss.geary.systems.query.Query import io.kotest.inspectors.forAll import io.kotest.matchers.collections.shouldContain diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt index dd82a5adc..c06f82259 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/ListenerLiveEntityModificationTests.kt @@ -5,8 +5,8 @@ import com.mineinabyss.geary.helpers.Comp1 import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.cachedQuery -import com.mineinabyss.geary.systems.listener +import com.mineinabyss.geary.systems.builders.cachedQuery +import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe From 6202329ec8522cea071ad073a996f0952a7a3565 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Sun, 25 Feb 2024 17:56:02 -0500 Subject: [PATCH 11/14] Fixes for orDefault and map accessors --- .../systems/accessors/AccessorOperations.kt | 18 ++++++-- .../geary/systems/accessors/Aliases.kt | 2 + .../accessors/type/ComponentAccessor.kt | 3 +- .../systems/accessors/type/MappedAccessor.kt | 1 + .../geary/systems/query/CachedQueryRunner.kt | 23 +++++----- .../geary/systems/query/QueriedEntity.kt | 4 ++ .../systems/accessors/AccessorHolderTest.kt | 30 ------------- .../systems/accessors/MappedAccessorTests.kt | 45 +++++++++++++++++++ 8 files changed, 79 insertions(+), 47 deletions(-) delete mode 100644 geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt create mode 100644 geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/MappedAccessorTests.kt diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt index e6851d04b..96e1e1480 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/AccessorOperations.kt @@ -15,7 +15,9 @@ abstract class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ protected inline fun QueriedEntity.get(): ComponentAccessor { - return addAccessor { NonNullComponentAccessor(cacheAccessors, null, this, componentId().withRole(HOLDS_DATA)) } + return addAccessor { + NonNullComponentAccessor(cacheAccessors, null, this, componentId().withRole(HOLDS_DATA)) + } } /** Accesses a data stored in a relation with kind [K] and target type [T], ensuring it is on the entity. */ @@ -26,6 +28,7 @@ abstract class AccessorOperations { inline fun QueriedEntity.addAccessor(create: () -> T): T { val accessor = create() accessors.add(accessor) + if (accessor is ComponentAccessor<*>) cachingAccessors.add(accessor) if (accessor.originalAccessor != null) accessors.remove(accessor.originalAccessor) return accessor } @@ -51,7 +54,9 @@ abstract class AccessorOperations { fun > A.map(mapping: (T) -> U): ReadOnlyAccessor { return queriedEntity.addAccessor { when (this) { - is FamilyMatching -> object : ReadOnlyAccessor by MappedAccessor(this, mapping), FamilyMatching by this {} + is FamilyMatching -> object : ReadOnlyAccessor by MappedAccessor(this, mapping), + FamilyMatching by this {} + else -> MappedAccessor(this, mapping) } } @@ -80,7 +85,14 @@ abstract class AccessorOperations { /** @see getRelations */ protected inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { - return addAccessor { RelationsWithDataAccessor(null, this, componentIdWithNullable(), componentIdWithNullable()) } + return addAccessor { + RelationsWithDataAccessor( + null, + this, + componentIdWithNullable(), + componentIdWithNullable() + ) + } } protected operator fun QueriedEntity.invoke(init: MutableFamily.Selector.And.() -> Unit) { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt index 2deed5d8f..93f6c6042 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/Aliases.kt @@ -1,5 +1,7 @@ package com.mineinabyss.geary.systems.accessors +import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.systems.query.CachedQueryRunner import com.mineinabyss.geary.systems.query.QueriedEntity import com.mineinabyss.geary.systems.query.Query import kotlin.properties.ReadOnlyProperty diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt index 5419cd653..97874cde5 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/ComponentAccessor.kt @@ -23,8 +23,7 @@ abstract class ComponentAccessor( protected var cachedIndex = -1 protected var cachedDataArray: MutableList = mutableListOf() - @PublishedApi - internal fun updateCache(archetype: Archetype) { + fun updateCache(archetype: Archetype) { cachedIndex = archetype.indexOf(id) if (cachedIndex != -1) cachedDataArray = archetype.componentData[cachedIndex] as MutableList } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt index 9b903063b..f5d0bd64f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/type/MappedAccessor.kt @@ -1,5 +1,6 @@ package com.mineinabyss.geary.systems.accessors.type +import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.QueriedEntity diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index 940478161..ddf005bcb 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -9,13 +9,7 @@ import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() val family = query.buildFamily() - val accessors = query.accessors.toTypedArray().filterIsInstance>() - - inline fun toList(crossinline map: T.() -> R): List { - val list = mutableListOf() - forEach { list.add(this.map()) } - return list - } + val cachingAccessors = query.cachingAccessors.toTypedArray() /** * Quickly iterates over all matched entities, running [run] for each. @@ -26,7 +20,7 @@ class CachedQueryRunner internal constructor(val query: T) { val matched = matchedArchetypes var n = 0 val size = matched.size // Get size ahead of time to avoid rerunning on entities that end up in new archetypes - val accessors = accessors + val accessors = cachingAccessors while (n < size) { val archetype = matched[n] archetype.isIterating = true @@ -47,9 +41,7 @@ class CachedQueryRunner internal constructor(val query: T) { inline fun map(crossinline run: T.() -> R): List { val deferred = mutableListOf() - forEach { - deferred.add(run()) - } + forEach { deferred.add(run()) } return deferred } @@ -62,10 +54,17 @@ class CachedQueryRunner internal constructor(val query: T) { inline fun mapWithEntity(crossinline run: T.() -> R): List> { val deferred = mutableListOf>() forEach { - deferred.add(Deferred(run(), archetype.getEntity(row))) + deferred.add(Deferred(run(), unsafeEntity)) } return deferred } + + @OptIn(UnsafeAccessors::class) + fun entities(): List { + val entities = mutableListOf() + forEach { entities.add(unsafeEntity) } + return entities + } } inline fun List>.execOnFinish(run: (data: R, entity: GearyEntity) -> Unit) { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt index a417c1a1a..06f63b7c3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueriedEntity.kt @@ -10,6 +10,7 @@ import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching +import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor open class EventQueriedEntity : QueriedEntity(cacheAccessors = false) open class QueriedEntity( @@ -23,6 +24,9 @@ open class QueriedEntity( @PublishedApi internal val accessors: MutableSet = mutableSetOf() + @PublishedApi + internal val cachingAccessors: MutableSet> = mutableSetOf() + fun buildFamily(): Family.Selector.And = family { accessors .filterIsInstance() diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt deleted file mode 100644 index 62addef15..000000000 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/AccessorHolderTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mineinabyss.geary.systems.accessors - -import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.systems.query.Query -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.junit.jupiter.api.Test - -internal class AccessorHolderTest : GearyTest() { - fun fancyQuery() = object : Query() { - val default by get().orDefault { "empty!" } - val mapped by get().map { it.toString() } - } - - @ExperimentalCoroutinesApi - @Test - fun fancyAccessors() { - val entity = entity() - entity.set(1) - //TODO reimplement test -// FancyQuery.firstOrNull { it.entity == entity } shouldBe null -// entity.set(1) -// FancyQuery.run { -// first { it.entity == entity }.apply { -// default shouldBe "empty!" -// mapped shouldBe "1" -// } -// } - } -} diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/MappedAccessorTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/MappedAccessorTests.kt new file mode 100644 index 000000000..6d4489e82 --- /dev/null +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/accessors/MappedAccessorTests.kt @@ -0,0 +1,45 @@ +package com.mineinabyss.geary.systems.accessors + +import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.systems.builders.cachedQuery +import com.mineinabyss.geary.systems.query.Query +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +internal class MappedAccessorTests : GearyTest() { + private class Marker + + private fun mappedQuery() = geary.cachedQuery(object : Query() { + val mapped by get().map { it.toString() } + }) + + private fun defaultingQuery() = geary.cachedQuery(object : Query() { + val default by get().orDefault { "empty!" } + override fun ensure() = this { has() } + }) + + @Test + fun `should correctly get mapped accessors`() { + entity { + set(1) + } + mappedQuery().forEach { + mapped shouldBe "1" + } + } + + @Test + fun `should correctly get default accessors`() { + entity { + set("Hello") + add() + } + entity { + add() + } + defaultingQuery().map { default }.shouldContainExactly("Hello", "empty!") + } +} From a305b250617d864507f03563fc3ecece9e662ce6 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 28 Feb 2024 10:00:18 -0500 Subject: [PATCH 12/14] Cleaning out some TODOs --- .../kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt | 8 ++++---- .../com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt | 4 +--- .../mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt | 7 ++----- .../mineinabyss/geary/systems/query/CachedQueryRunner.kt | 5 +++-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt index 39d4a2d25..c414ca3ec 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUIDTracking.kt @@ -1,8 +1,8 @@ package com.mineinabyss.geary.uuid import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.uuid.systems.createTrackUUIDOnAddSystem -import com.mineinabyss.geary.uuid.systems.createUntrackUuidOnRemoveSystem +import com.mineinabyss.geary.uuid.systems.createTrackUUIDOnAddListener +import com.mineinabyss.geary.uuid.systems.createUntrackUuidOnRemoveListener import com.mineinabyss.idofront.di.DI val uuid2Geary by DI.observe() @@ -11,7 +11,7 @@ object UUIDTracking : GearyAddonWithDefault { override fun default() = SimpleUUID2GearyMap() override fun UUID2GearyMap.install() { - createTrackUUIDOnAddSystem() - createUntrackUuidOnRemoveSystem() + createTrackUUIDOnAddListener() + createUntrackUuidOnRemoveListener() } } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt index 23c0de06f..8d39ffb9e 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/TrackUuidOnAdd.kt @@ -2,15 +2,13 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid import com.benasher44.uuid.uuid4 -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash import com.mineinabyss.geary.uuid.uuid2Geary -@OptIn(UnsafeAccessors::class) -fun createTrackUUIDOnAddSystem() = geary.listener(object : ListenerQuery() { +fun createTrackUUIDOnAddListener() = geary.listener(object : ListenerQuery() { var uuid by get() val regenerateUUIDOnClash by get().orNull() override fun ensure() = event.anySet(::uuid) diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt index aa1813319..ae4f7d82c 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/systems/UnTrackUuidOnRemove.kt @@ -7,10 +7,7 @@ import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery import com.mineinabyss.geary.uuid.uuid2Geary -//TODO fix -fun createUntrackUuidOnRemoveSystem() = geary.listener(object : ListenerQuery() { +fun createUntrackUuidOnRemoveListener() = geary.listener(object : ListenerQuery() { val uuid by get() override fun ensure() = event.invoke { has() } -}).exec { - uuid2Geary.remove(uuid) -} +}).exec { uuid2Geary.remove(uuid) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt index ddf005bcb..ef6013107 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQueryRunner.kt @@ -4,7 +4,6 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor class CachedQueryRunner internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() @@ -24,10 +23,12 @@ class CachedQueryRunner internal constructor(val query: T) { while (n < size) { val archetype = matched[n] archetype.isIterating = true + + // We disallow entity archetype modifications while iterating, but allow creating new entities. + // These will always end up at the end of the archetype list so we just don't iterate over them. val upTo = archetype.size var row = 0 query.originalArchetype = archetype - // TODO upTo isn't perfect for cases where entities may be added or removed in the same iteration accessors.fastForEach { it.updateCache(archetype) } while (row < upTo) { query.originalRow = row From 994181cc728b8232c28f1a214cff7449986c6d6e Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 28 Feb 2024 19:51:23 +0100 Subject: [PATCH 13/14] Some more todos! --- .../mineinabyss/geary/autoscan/AutoScanner.kt | 2 +- .../systems/ParseRelationOnPrefab.kt | 9 ++-- .../geary/helpers/ArchetypeHelpers.kt | 6 +-- .../geary/systems/query/ListenerQuery.kt | 48 ++++++++++++------- .../mineinabyss/geary/systems/query/Query.kt | 7 ++- .../geary/systems/FamilyMatchingTest.kt | 9 ++-- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScanner.kt b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScanner.kt index f23d328a7..a334e8060 100644 --- a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScanner.kt +++ b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScanner.kt @@ -26,7 +26,7 @@ interface AutoScanner { override fun installSystems() { scannedSystems.asSequence() .mapNotNull { it.objectInstance ?: runCatching { it.createInstance() }.getOrNull() } - .filterIsInstance() + .filterIsInstance>() .onEach { geary.pipeline.addSystem(it) } .map { it::class.simpleName } .let { diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt index 05b732542..1ef955df1 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseRelationOnPrefab.kt @@ -1,19 +1,18 @@ package com.mineinabyss.geary.prefabs.configuration.systems +import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab import com.mineinabyss.geary.systems.builders.listener import com.mineinabyss.geary.systems.query.ListenerQuery - fun createParseRelationOnPrefabListener() = geary.listener(object : ListenerQuery() { - var relation by get() + val relation by get() override fun ensure() = event.anySet(::relation) }).exec { try { - //TODO - val rel: RelationOnPrefab = relation -// entity.setRelation(relation.value, entity.parseEntity(relation.key).id) + val target = entity.lookup(relation.target)?.id ?: return@exec + entity.setRelation(componentId(relation.data::class), target, relation.data) } finally { entity.remove() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt index 788572af0..fcb8d500c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt @@ -1,10 +1,8 @@ package com.mineinabyss.geary.helpers -import com.mineinabyss.geary.modules.ArchetypeEngineModule -import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.archetypes -// TODO context to avoid cast fun EntityType.getArchetype(): Archetype = - (geary as ArchetypeEngineModule).archetypeProvider.getArchetype(this) + archetypes.archetypeProvider.getArchetype(this) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt index 072fa407e..156691c84 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/ListenerQuery.kt @@ -1,7 +1,9 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.MutableFamily import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import kotlin.reflect.KProperty @@ -23,35 +25,45 @@ abstract class ListenerQuery : Query() { source = source.buildFamily(), ) - /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ - protected fun onAdd(vararg props: KProperty<*>) { - - } - - /** Fires when an entity has a component of type [T] set, only if it was not set before. */ - protected fun onFirstSet(vararg props: KProperty<*>) { - - } - @OptIn(UnsafeAccessors::class) val QueriedEntity.entity get() = unsafeEntity - protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) { - anySet(*props.mapNotNull { this@ListenerQuery.props[it.name] }.toTypedArray()) + internal fun getAccessorsFor(vararg props: KProperty<*>): Array { + return props.mapNotNull { this@ListenerQuery.props[it.name] }.toTypedArray() } + protected fun EventQueriedEntity.anySet(vararg props: KProperty<*>) = anySet(*getAccessorsFor(*props)) - /** Fires when an entity has a component of type [T] set or updated. */ - protected fun EventQueriedEntity.anySet(vararg props: Accessor) { + protected fun EventQueriedEntity.anyAdded(vararg props: KProperty<*>) = anyAdded(*getAccessorsFor(*props)) + + protected fun EventQueriedEntity.anyFirstSet(vararg props: KProperty<*>) = anyFirstSet(*getAccessorsFor(*props)) + + protected inline fun EventQueriedEntity.forEachAccessorComponent( + props: Collection, + crossinline run: MutableFamily.Selector.And.(ComponentId) -> Unit + ) { invoke { this@ListenerQuery.accessors.intersect(props.toSet()) .asSequence() .filterIsInstance() .mapNotNull { it.family } .flatMap { it.components } - .forEach { component -> - onSet(component) - // TODO do we error here if not, this isn't really typesafe? - } + .forEach { run(it) } } } + + /** Fires when an entity has a component of type [T] set or updated. */ + protected fun EventQueriedEntity.anySet(vararg props: Accessor) { + forEachAccessorComponent(props.toSet()) { onSet(it) } + } + + /** Fires when an entity has a component of type [T] added, updates are not considered since no data changes. */ + protected fun EventQueriedEntity.anyAdded(vararg props: Accessor) { + forEachAccessorComponent(props.toSet()) { onAdd(it) } + } + + /** Fires when an entity has a component of type [T] set, only if it was not set before. */ + protected fun EventQueriedEntity.anyFirstSet(vararg props: Accessor) { + forEachAccessorComponent(props.toSet()) { onFirstSet(it) } + } + } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt index dea15d1b4..05501edc1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/Query.kt @@ -1,15 +1,12 @@ package com.mineinabyss.geary.systems.query -import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.type.ComponentAccessor import kotlin.reflect.KProperty abstract class Query : QueriedEntity(cacheAccessors = true) { - //TODO duplicate with EventQuery /** Automatically matches families for any accessor that's supposed to match a family. */ - operator fun T.provideDelegate( + operator fun T.provideDelegate( thisRef: Any, prop: KProperty<*> ): T { @@ -23,6 +20,8 @@ abstract class Query : QueriedEntity(cacheAccessors = true) { ensure() } + // Optional helpers for avoiding delegates in accessors + inline operator fun ComponentAccessor.invoke(): T = get(this@Query) inline fun ComponentAccessor.set(value: T) = set(this@Query, value) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt index d7b21c806..f5fa7c60e 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/systems/FamilyMatchingTest.kt @@ -1,8 +1,11 @@ package com.mineinabyss.geary.systems +import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.HOLDS_DATA +import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.helpers.getArchetype import com.mineinabyss.geary.helpers.tests.GearyTest import com.mineinabyss.geary.modules.archetypes import com.mineinabyss.geary.modules.geary @@ -28,12 +31,6 @@ class FamilyMatchingTest : GearyTest() { val root = archetypes.archetypeProvider.rootArchetype val correctArchetype = root + stringId + intId - @Test - fun `family type is correct`() { - // TODO families can are wrapped by accessors now, so components won't be directly on it -// EntityType(system.family.components).getArchetype() shouldBe root + stringId - } - @Test fun `archetypes have been matched correctly`() { system.runner.matchedArchetypes shouldContain correctArchetype From 1ff6487292cb40457b382dd487247da0b4a50741 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Tue, 12 Mar 2024 20:18:19 -0400 Subject: [PATCH 14/14] Version bump --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ca379dcb7..20824fd4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=com.mineinabyss -version=0.25 +version=0.26 # Workaround for dokka builds failing on CI, see https://github.com/Kotlin/dokka/issues/1405 #org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m idofrontVersion=0.22.0