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 e6851d04..96e1e148 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 2deed5d8..93f6c604 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 5419cd65..97874cde 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 9b903063..f5d0bd64 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 94047816..ddf005bc 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 a417c1a1..06f63b7c 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 62addef1..00000000 --- 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 00000000..6d4489e8 --- /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!") + } +}