Skip to content

Commit

Permalink
feat: OnFirstSet event
Browse files Browse the repository at this point in the history
feat: Add cacheGroupedBy query
fix: Don't double count entities in cacheGrouped/AssociatedBy by observing correct events, and limiting to involved components
fix: OnRemove not firing when entity removed
  • Loading branch information
0ffz committed Apr 26, 2024
1 parent 867db5d commit 3398cd9
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 26 deletions.
11 changes: 5 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ allprojects {
useJUnitPlatform()
}
}
js(IR) {
// TODO Other targets are missing some implementations like Roaring Bitmaps, but we hope someone
// interested can add support in the future. Thus, we force a target that doesn't actually compile
// so we can't accidentally use jvm-only code in the common target.
compilations.configureEach { compileKotlinTask.enabled = false }
}
// TODO Other targets are missing some implementations like Roaring Bitmaps, but we hope someone
// interested can add support in the future. Thus, we force a target that doesn't actually compile
// so we can't accidentally use jvm-only code in the common target.
linuxX64()

sourceSets {
all {
languageSettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Components {
val couldHaveChildren = componentId<CouldHaveChildren>()
val onAdd = componentId<OnAdd>()
val onSet = componentId<OnSet>()
val onFirstSet = componentId<OnFirstSet>()
val onUpdate = componentId<OnUpdate>()
val onRemove = componentId<OnRemove>()
val onExtend = componentId<OnExtend>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,10 @@ class Archetype internal constructor(
if (addIndex >= 0) {
componentData[addIndex][row] = data
if (callEvent) {
callComponentModifyEvent(comps.onUpdate, componentId, row, onUpdated)
callComponentModifyEvent(comps.onSet, componentId, row, onUpdated)
callComponentModifyEvent(comps.onUpdate, componentId, row) { arch, row ->
// Potential archetype modification can occur after event
arch.callComponentModifyEvent(comps.onSet, componentId, row, onUpdated)
}
}
return
}
Expand All @@ -279,7 +281,11 @@ class Archetype internal constructor(
removeEntity(row)

// Component add listeners must query the target, this is an optimization
if (callEvent) moveTo.callComponentModifyEvent(comps.onSet, componentId, newRow, onUpdated)
if (callEvent) {
moveTo.callComponentModifyEvent(comps.onSet, componentId, newRow) { arch, row ->
arch.callComponentModifyEvent(comps.onFirstSet, componentId, row, onUpdated)
}
}
else onUpdated(moveTo, newRow)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class EntityByArchetypeProvider(
}
}

// Emit remove events for each component (they get cleared all at once after this)
entity.type.forEach { compId ->
if (entity.has(compId)) entity.emit(event = geary.components.onRemove, involving = compId)
}

records.runOn(entity) { archetype, row ->
archetype.removeEntity(row)
records.remove(entity)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,62 @@
package com.mineinabyss.geary.events.queries

import com.mineinabyss.geary.datatypes.Entity
import com.mineinabyss.geary.events.types.OnAdd
import com.mineinabyss.geary.events.types.OnFirstSet
import com.mineinabyss.geary.events.types.OnRemove
import com.mineinabyss.geary.modules.GearyModule
import com.mineinabyss.geary.systems.builders.observe
import com.mineinabyss.geary.systems.query.Query
import com.mineinabyss.geary.systems.query.ShorthandQuery

fun <T, Q : Query> GearyModule.cacheAsMap(query: Q, associateBy: (Q) -> T): ObserveQueryAssociatedBy<T, Q> {
return object : ObserveQueryAssociatedBy<T, Q>(query, this) {
override fun associateBy(query: Q): T = associateBy(query)
fun <T, Q : ShorthandQuery> GearyModule.cacheGroupedBy(
query: Q,
groupBy: ObserverContext.(Q) -> T
): QueryGroupedBy<T, Q> {
return object : QueryGroupedBy<T, Q>(query, this) {
override fun ObserverContext.groupBy(query: Q): T = groupBy(query)
}
}

abstract class ObserveQueryAssociatedBy<T, Q : Query>(private val query: Q, geary: GearyModule) {
val map = mutableMapOf<T, Entity>()
abstract fun associateBy(query: Q): T
fun <T, Q : ShorthandQuery> GearyModule.cacheAssociatedBy(
query: Q,
associateBy: ObserverContext.(Q) -> T
): QueryAssociatedBy<T, Q> {
return object : QueryAssociatedBy<T, Q>(query, this) {
override fun ObserverContext.associateBy(query: Q): T = associateBy(query)
}
}

abstract class QueryGroupedBy<T, Q : ShorthandQuery>(private val query: Q, geary: GearyModule) {
private val map = mutableMapOf<T, MutableList<Entity>>()

abstract fun ObserverContext.groupBy(query: Q): T

operator fun get(key: T): List<Entity> = map[key] ?: listOf()

private fun add(key: T, value: Entity) {
map.getOrPut(key) { mutableListOf() }.add(value)
}

private fun remove(key: T, value: Entity) {
map[key]?.remove(value)
}

init {
geary.observe<OnFirstSet>().involving(query).exec {
add(groupBy(it), entity)
}
geary.observe<OnRemove>().involving(query).exec {
remove(groupBy(it), entity)
}
}
}

abstract class QueryAssociatedBy<T, Q : ShorthandQuery>(private val query: Q, geary: GearyModule) {
private val map = mutableMapOf<T, Entity>()

abstract fun ObserverContext.associateBy(query: Q): T

operator fun get(key: T): Entity? = map[key]
operator fun set(key: T, value: Entity) {
private operator fun set(key: T, value: Entity) {
map[key] = value
}

Expand All @@ -27,10 +65,10 @@ abstract class ObserveQueryAssociatedBy<T, Q : Query>(private val query: Q, gear
}

init {
geary.observe<OnAdd>().exec(query) {
geary.observe<OnFirstSet>().involving(query).exec {
map[associateBy(it)] = entity
}
geary.observe<OnRemove>().exec(query) {
geary.observe<OnRemove>().involving(query).exec {
map.remove(associateBy(it))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class OnAdd

class OnSet

class OnFirstSet

class OnRemove

class OnUpdate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@ fun query(match: MutableFamily.Selector.And.() -> Unit) = object : Query() {
override fun ensure() = this { add(family(match)) }
}

inline fun <reified A : Any> query(size1: QueryShorthands.Size1? = null) =
object : ShorthandQuery1<A>() {
override val involves = entityTypeOf(cId<A>())
inline fun <reified A : Any> query(
size1: QueryShorthands.Size1? = null,
noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null
) = object : ShorthandQuery1<A>() {
override val involves = entityTypeOf(cId<A>())
override fun ensure() {
filterFamily?.let { this { it() } }
}

val comp1 by get<A>()
val comp1 by get<A>()

override fun component1() = comp1
}
override fun component1() = comp1
}

inline fun <reified A : Any, reified B : Any> query(size2: QueryShorthands.Size2? = null) =
object : ShorthandQuery2<A, B>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mineinabyss.geary.observers

import com.mineinabyss.geary.events.queries.cacheAssociatedBy
import com.mineinabyss.geary.helpers.entity
import com.mineinabyss.geary.helpers.tests.GearyTest
import com.mineinabyss.geary.modules.geary
import com.mineinabyss.geary.systems.query.query
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test

class QueryAsMapTest : GearyTest() {
@Test
fun `should correctly track entity in map`() {
val map = geary.cacheAssociatedBy(query<String>()) { (string) -> string }

entity {
set("Hello world")
map["Hello world"] shouldBe this
remove<String>()
map["Hello world"] shouldBe null
set("Hello world")
removeEntity()
map["Hello world"] shouldBe null
}
}
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ 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.23.0
kotlin.native.ignoreDisabledTargets=true

0 comments on commit 3398cd9

Please sign in to comment.