From 3d6d0965781caa7665524f1990688fad13a2672f Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Tue, 15 Oct 2024 01:29:30 -0400 Subject: [PATCH 1/9] refactor: Swap to using proper dependency injection throughout in anticipation of multi-world ticking support --- README.md | 2 +- .../mineinabyss/geary/actions/ActionGroup.kt | 4 +- .../mineinabyss/geary/actions/GearyActions.kt | 17 +-- .../event_binds/ParseEntityObservers.kt | 5 +- .../geary/actions/event_binds/Passive.kt | 6 +- .../actions/ConfigEntityObserversTests.kt | 4 +- .../geary/autoscan/AutoScannerDSL.kt | 3 +- .../com/mineinabyss/geary/prefabs/Prefabs.kt | 47 ------- .../mineinabyss/geary/prefabs/PrefabsDSL.kt | 3 +- .../geary/prefabs/PrefabsModule.kt | 40 ++++++ .../systems/CopyToInstancesSystem.kt | 2 +- .../systems/ParseChildOnPrefab.kt | 4 +- .../systems/ParseInstancesOnPrefab.kt | 2 +- .../configuration/systems/ParseReEmitEvent.kt | 2 +- .../systems/ParseRelationOnPrefab.kt | 2 +- .../systems/ParseRelationWithDataSystem.kt | 2 +- .../prefabs/systems/InheritPrefabsOnLoad.kt | 3 +- .../systems/TrackPrefabsByKeySystem.kt | 2 +- .../geary/prefabs/CopyToInstancesTest.kt | 5 +- .../EntitySerializationExtensions.kt | 10 +- .../geary/serialization/FileSystemAddon.kt | 10 +- .../serialization/SerializableComponents.kt | 112 +++++++++++----- .../dsl/SerializableComponentsDSL.kt | 71 ---------- .../dsl/WithCommonComponentNames.kt | 3 +- .../dsl/builders/FormatsBuilder.kt | 6 +- .../geary/serialization/helpers/Component.kt | 7 +- .../serializers/GearyEntitySerializer.kt | 10 +- .../PolymorphicListAsMapSerializer.kt | 6 +- .../serializers/SerializableComponentId.kt | 1 - .../geary/serialization/formats/YamlFormat.kt | 27 ++-- .../mineinabyss/geary/uuid/UUID2GearyMap.kt | 25 ++-- .../mineinabyss/geary/uuid/UUIDTracking.kt | 21 +-- .../geary/uuid/systems/TrackUuidOnAdd.kt | 11 +- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 8 +- .../mineinabyss/geary/addons/Namespaced.kt | 4 +- .../geary/addons/dsl/GearyAddon.kt | 97 +++++++++++++- .../com/mineinabyss/geary/datatypes/Entity.kt | 114 +++++++++-------- .../geary/datatypes/EntityIdArray.kt | 37 ++++++ .../geary/datatypes/EntityStack.kt | 12 +- .../mineinabyss/geary/datatypes/EntityType.kt | 2 +- .../mineinabyss/geary/datatypes/Relation.kt | 15 ++- .../geary/datatypes/family/MutableFamily.kt | 77 ++++++----- .../geary/datatypes/maps/ArrayTypeMap.kt | 26 ++-- .../datatypes/maps/Family2ObjectArrayMap.kt | 14 +- .../datatypes/maps/SynchronizedTypeMap.kt | 9 +- .../geary/datatypes/maps/TypeMap.kt | 7 +- .../geary/engine/ComponentProvider.kt | 13 +- .../mineinabyss/geary/engine/Components.kt | 36 +++--- .../geary/engine/EntityMutateOperations.kt | 13 +- .../geary/engine/EntityProvider.kt | 9 +- .../geary/engine/EntityReadOperations.kt | 12 +- .../mineinabyss/geary/engine/PipelineImpl.kt | 6 +- .../mineinabyss/geary/engine/QueryManager.kt | 8 +- .../geary/engine/archetypes/Archetype.kt | 68 +++++----- .../engine/archetypes/ArchetypeEngine.kt | 11 +- .../archetypes/ArchetypeQueryManager.kt | 25 +++- .../archetypes/ComponentAsEntityProvider.kt | 25 ++-- .../archetypes/EntityByArchetypeProvider.kt | 87 +++++++------ .../archetypes/SimpleArchetypeProvider.kt | 35 +++-- .../operations/ArchetypeMutateOperations.kt | 44 ++++--- .../operations/ArchetypeReadOperations.kt | 32 +++-- .../geary/helpers/ArchetypeHelpers.kt | 6 +- .../geary/helpers/ComponentHelpers.kt | 5 +- .../geary/helpers/EngineHelpers.kt | 31 +++-- .../geary/helpers/EntityHelpers.kt | 9 +- .../mineinabyss/geary/helpers/Relationship.kt | 4 +- .../mineinabyss/geary/helpers/WithOperator.kt | 2 +- .../geary/modules/ArchetypeEngineModule.kt | 58 +++++---- .../com/mineinabyss/geary/modules/Geary.kt | 81 ++++++++++++ .../geary/modules/GearyConfiguration.kt | 121 +++++++++--------- .../mineinabyss/geary/modules/GearyModule.kt | 47 ++++--- .../mineinabyss/geary/modules/GearySetup.kt | 27 ++++ .../geary/modules/MutableAddons.kt | 23 ++++ .../geary/modules/TestEngineModule.kt | 11 +- .../geary/observers/ArchetypeEventRunner.kt | 27 ++-- .../geary/observers/EventRunner.kt | 3 +- .../geary/observers/EventToObserversMap.kt | 7 +- .../mineinabyss/geary/observers/Observer.kt | 3 +- .../geary/observers/ObserverList.kt | 12 +- .../observers/builders/ObserverBuilder.kt | 5 +- .../builders/ObserverEventsBuilder.kt | 54 ++++---- .../geary/observers/entity/EntityObserver.kt | 15 ++- .../geary/observers/events/EntityEvents.kt | 4 +- .../observers/queries/CacheQueryAsMap.kt | 15 +-- .../systems/accessors/AccessorOperations.kt | 25 ++-- .../systems/accessors/RelationWithData.kt | 5 +- .../accessors/type/ComponentAccessor.kt | 5 +- .../accessors/type/RelationsAccessor.kt | 5 +- .../type/RelationsWithDataAccessor.kt | 5 +- .../geary/systems/builders/GlobalFunctions.kt | 35 ----- .../geary/systems/query/CachedQuery.kt | 9 +- .../geary/systems/query/QueriedEntity.kt | 22 ++-- .../mineinabyss/geary/systems/query/Query.kt | 4 +- .../geary/systems/query/QueryShorthands.kt | 39 +++--- .../datatypes/Family2ObjectArrayMapTest.kt | 33 +++-- .../mineinabyss/geary/datatypes/FamilyTest.kt | 3 +- .../geary/helpers/tests/GearyTest.kt | 7 +- .../geary/systems/FamilyMatchingTest.kt | 5 - gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43583 bytes 99 files changed, 1234 insertions(+), 859 deletions(-) delete mode 100644 addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt create mode 100644 addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt delete mode 100644 addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/SerializableComponentsDSL.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt diff --git a/README.md b/README.md index 82e486ab0..79022fc55 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Read our [Quickstart guide](https://wiki.mineinabyss.com/geary/quickstart/) to s data class Position(var x: Double, var y: Double) data class Velocity(var x: Double, var y: Double) -fun GearyModule.updatePositionSystem() = system(query()) +fun Geary.updatePositionSystem() = system(query()) .every(interval = 20.milliseconds) .exec { (position, velocity) -> // We can access our components like regular variables! diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt index ab6b2e2dd..07a3e30ee 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt @@ -1,9 +1,11 @@ package com.mineinabyss.geary.actions +import co.touchlab.kermit.Logger import com.mineinabyss.geary.actions.actions.EmitEventAction import com.mineinabyss.geary.actions.actions.EnsureAction import com.mineinabyss.geary.actions.event_binds.* import com.mineinabyss.geary.actions.expressions.Expression +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer @@ -88,7 +90,7 @@ class ActionGroup( Expression.parseExpression(comp.expression, serializersModule) as Expression> comp is ActionEnvironment -> environment = comp.environment - action != null -> geary.logger.w { "Multiple actions defined in one block!" } + action != null -> Geary.w { "Multiple actions defined in one block!" } else -> action = EmitEventAction.wrapIfNotAction(comp) } } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/GearyActions.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/GearyActions.kt index 0c7505ca9..d1e059500 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/GearyActions.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/GearyActions.kt @@ -2,18 +2,11 @@ package com.mineinabyss.geary.actions import com.mineinabyss.geary.actions.event_binds.bindEntityObservers import com.mineinabyss.geary.actions.event_binds.parsePassive -import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.addons.dsl.createAddon -class GearyActions { - companion object : GearyAddonWithDefault { - override fun default() = GearyActions() - - override fun GearyActions.install() { - geary.run { - bindEntityObservers() - parsePassive() - } - } +val GearyActions = createAddon("Geary Actions") { + onStart { + bindEntityObservers() + parsePassive() } } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt index 67870bdf0..a8853489e 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt @@ -3,13 +3,12 @@ package com.mineinabyss.geary.actions.event_binds import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.execute import com.mineinabyss.geary.datatypes.EntityType -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.entity.observe import com.mineinabyss.geary.observers.events.OnSet -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.bindEntityObservers() = observe() +fun Geary.bindEntityObservers() = observe() .involving(query()) .exec { (observers) -> observers.observers.forEach { observer -> diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt index 20ded7fd3..6991f3dcd 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt @@ -6,12 +6,10 @@ import com.mineinabyss.geary.actions.execute import com.mineinabyss.geary.actions.serializers.DurationSerializer import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.SerializableComponentId -import com.mineinabyss.geary.systems.builders.observe -import com.mineinabyss.geary.systems.builders.system import com.mineinabyss.geary.systems.query.query import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer @@ -37,7 +35,7 @@ class Passive( ) } -fun GearyModule.parsePassive() = observe() +fun Geary.parsePassive() = observe() .involving(query()) .exec { (passive) -> passive.systems.forEach { systemBind -> diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt index 24fa165bb..a4cfe77d8 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt @@ -7,6 +7,7 @@ import com.mineinabyss.geary.serialization.dsl.serialization import com.mineinabyss.geary.serialization.dsl.withCommonComponentNames import com.mineinabyss.geary.serialization.formats.YamlFormat import com.mineinabyss.geary.serialization.serializableComponents +import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer import com.mineinabyss.geary.systems.builders.observeWithData import com.mineinabyss.idofront.di.DI @@ -42,8 +43,7 @@ class ConfigEntityObserversTests { component(MyComp.serializer()) } } - } - geary.pipeline.runStartupTasks() + }.start() } @Test diff --git a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt index 1396e497d..2e63d1801 100644 --- a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt +++ b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt @@ -5,6 +5,7 @@ import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.modules.GearyConfiguration import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.GearySetup import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.serialization.dsl.serialization import kotlinx.serialization.* @@ -19,7 +20,7 @@ import kotlin.reflect.jvm.kotlinFunction import kotlin.reflect.typeOf @GearyDSL -fun GearyConfiguration.autoscan( +fun GearySetup.autoscan( classLoader: ClassLoader, vararg limitToPackages: String, configure: AutoScannerDSL.() -> Unit 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 deleted file mode 100644 index aa4310d08..000000000 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/Prefabs.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.mineinabyss.geary.prefabs - -import com.mineinabyss.geary.addons.GearyPhase -import com.mineinabyss.geary.addons.Namespaced -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.createInheritPrefabsOnLoadListener -import com.mineinabyss.geary.prefabs.systems.createTrackPrefabsByKeyListener -import com.mineinabyss.idofront.di.DI - -val prefabs by DI.observe() - -interface Prefabs { - val manager: PrefabManager - val loader: PrefabLoader - - companion object : GearyAddonWithDefault { - override fun default() = object : Prefabs { - override val manager = PrefabManager() - override val loader: PrefabLoader = PrefabLoader() - } - - - override fun Prefabs.install() { - geary.run { - createInheritPrefabsOnLoadListener() - createParseChildOnPrefabListener() - createParseChildrenOnPrefabListener() - createParseInstancesOnPrefabListener() - createParseRelationOnPrefabListener() - createParseRelationWithDataListener() - createTrackPrefabsByKeyListener() - createCopyToInstancesSystem() - reEmitEvent() - } - geary.pipeline.runOnOrAfter(GearyPhase.INIT_ENTITIES) { - loader.loadOrUpdatePrefabs() - } - } - } -} - -@GearyDSL -fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit) = - gearyConf.install(Prefabs).also { PrefabsDSL(this).configure() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt index d183d0a08..d0f1e75d9 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt @@ -7,10 +7,9 @@ import okio.Path @GearyDSL class PrefabsDSL( + private val loader: PrefabLoader, private val namespaced: Namespaced ) { - private val loader = prefabs.loader - /** Loads prefab entities from all files inside a [directory][from], into a given [namespace] */ fun from( vararg from: Path, diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt new file mode 100644 index 000000000..fd23e370f --- /dev/null +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt @@ -0,0 +1,40 @@ +package com.mineinabyss.geary.prefabs + +import com.mineinabyss.geary.addons.Namespaced +import com.mineinabyss.geary.addons.dsl.GearyDSL +import com.mineinabyss.geary.addons.dsl.createAddon +import com.mineinabyss.geary.prefabs.configuration.systems.* +import com.mineinabyss.geary.prefabs.systems.createInheritPrefabsOnLoadListener +import com.mineinabyss.geary.prefabs.systems.createTrackPrefabsByKeyListener + +interface PrefabsModule { + val manager: PrefabManager + val loader: PrefabLoader +} + +val Prefabs = createAddon("Prefabs", { + object : PrefabsModule { + override val manager = PrefabManager() + override val loader: PrefabLoader = PrefabLoader() + } +}) { + systems { + createInheritPrefabsOnLoadListener() + createParseChildOnPrefabListener() + createParseChildrenOnPrefabListener() + createParseInstancesOnPrefabListener() + createParseRelationOnPrefabListener() + createParseRelationWithDataListener() + createTrackPrefabsByKeyListener() + createCopyToInstancesSystem() + reEmitEvent() + } + + entities { + configuration.loader.loadOrUpdatePrefabs() + } +} + +@GearyDSL +fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit) = + setup.install(Prefabs) { PrefabsDSL(loader, this@prefabs).configure() } 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 159db7c42..96efff8a8 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 @@ -5,7 +5,7 @@ import com.mineinabyss.geary.observers.events.OnExtend import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances import com.mineinabyss.geary.systems.builders.observeWithData -fun GearyModule.createCopyToInstancesSystem() = observeWithData() +fun Geary.createCopyToInstancesSystem() = observeWithData() .exec { val copy = event.baseEntity.get() ?: return@exec copy.decodeComponentsTo(entity) 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 cb0ded686..01c6fa460 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,7 +13,7 @@ import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.createParseChildOnPrefabListener() = observe() +fun Geary.createParseChildOnPrefabListener() = observe() .involving(query()) .exec { (child) -> entity { @@ -23,7 +23,7 @@ fun GearyModule.createParseChildOnPrefabListener() = observe() entity.remove() } -fun GearyModule.createParseChildrenOnPrefabListener() = observe() +fun Geary.createParseChildrenOnPrefabListener() = observe() .involving(query()) .exec { (children) -> children.nameToComponents.forEach { (name, components) -> diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt index 1ffc330db..8039925b7 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt @@ -11,7 +11,7 @@ import com.mineinabyss.geary.prefabs.configuration.components.Prefab import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.createParseInstancesOnPrefabListener() = observe() +fun Geary.createParseInstancesOnPrefabListener() = observe() .involving(query()).exec { (instances, prefabKey) -> entity.addRelation() instances.nameToComponents.forEach { (name, components) -> diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt index ea4f5fc26..f05325c7b 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt @@ -5,7 +5,7 @@ import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.prefabs.configuration.components.ReEmitEvent import com.mineinabyss.geary.systems.builders.observeWithData -fun GearyModule.reEmitEvent() = observeWithData().exec { +fun Geary.reEmitEvent() = observeWithData().exec { entity.getRelationsByKind(event.findByRelationKind.id).forEach { relation -> val entity = relation.target.toGeary() if (entity.exists()) entity.emit(event = event.dataComponentId, data = event.data) 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 43c8bf7d5..eb80bd3a5 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,7 +7,7 @@ import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.createParseRelationOnPrefabListener() = observe() +fun Geary.createParseRelationOnPrefabListener() = observe() .involving(query()).exec { (relation) -> try { val target = entity.lookup(relation.target)?.id ?: return@exec 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 9903ec9a4..e97cf4003 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 @@ -6,7 +6,7 @@ import com.mineinabyss.geary.systems.accessors.RelationWithData import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.createParseRelationWithDataListener() = observe() +fun Geary.createParseRelationWithDataListener() = observe() .involving(query>()).exec { (relationWithData) -> val entity = entity val data = relationWithData.data 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 0b6378000..2208c4dba 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,9 +1,10 @@ package com.mineinabyss.geary.prefabs.systems +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.prefabs.events.PrefabLoaded import com.mineinabyss.geary.prefabs.helpers.inheritPrefabsIfNeeded import com.mineinabyss.geary.systems.builders.observe -fun GearyModule.createInheritPrefabsOnLoadListener() = observe() +fun Geary.createInheritPrefabsOnLoadListener() = observe() .exec { entity.inheritPrefabsIfNeeded() } 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 a58a79416..b787647e8 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 @@ -8,7 +8,7 @@ import com.mineinabyss.geary.prefabs.prefabs import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -fun GearyModule.createTrackPrefabsByKeyListener() = observe() +fun Geary.createTrackPrefabsByKeyListener() = observe() .involving(query()).exec { (key) -> prefabs.manager.registerPrefab(key, entity) entity.addRelation() diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt index becb93d1a..03237c469 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt @@ -5,8 +5,8 @@ import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances -import com.mineinabyss.geary.serialization.dsl.serialization import com.mineinabyss.geary.serialization.getAllPersisting +import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.idofront.di.DI import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe @@ -27,8 +27,7 @@ class CopyToInstancesTest { component(Int.serializer()) } } - } - geary.pipeline.runStartupTasks() + }.start() } @Test diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/EntitySerializationExtensions.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/EntitySerializationExtensions.kt index d655e50a0..ce81a52f3 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/EntitySerializationExtensions.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/EntitySerializationExtensions.kt @@ -4,17 +4,9 @@ import com.mineinabyss.geary.annotations.optin.DangerousComponentOperation import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.observers.events.OnAdd -import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.serialization.components.Persists -import com.mineinabyss.geary.systems.builders.observe -import com.mineinabyss.geary.systems.query.query -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract import kotlin.reflect.KClass -import kotlin.reflect.typeOf /** @@ -28,7 +20,7 @@ inline fun Entity.setPersisting( noEvent: Boolean = false, ): T { set(component, kClass, noEvent) - setRelation(serializableComponents.persists, componentId(kClass), Persists(), noEvent) + setRelation(world.getAddon(SerializableComponents).persists, world.componentId(kClass), Persists(), noEvent) return component } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt index 872853a1c..f9bc81427 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt @@ -1,14 +1,10 @@ package com.mineinabyss.geary.serialization -import com.mineinabyss.geary.addons.dsl.GearyAddon +import com.mineinabyss.geary.addons.dsl.createAddon import com.mineinabyss.idofront.di.DI import okio.FileSystem val fileSystem by DI.observe() -interface FileSystemAddon { - companion object : GearyAddon { - override fun FileSystem.install() { - } - } -} + +fun FileSystemAddon(fileSystem: FileSystem) = createAddon("File System", { fileSystem }) diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt index 7bf9ad474..412218d9b 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt @@ -1,49 +1,101 @@ package com.mineinabyss.geary.serialization -import com.mineinabyss.geary.addons.GearyPhase -import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault +import com.mineinabyss.geary.addons.dsl.GearyDSL +import com.mineinabyss.geary.addons.dsl.createAddon +import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.GearySetup import com.mineinabyss.geary.serialization.components.Persists -import com.mineinabyss.geary.serialization.dsl.SerializableComponentsDSL import com.mineinabyss.geary.serialization.dsl.builders.ComponentSerializersBuilder import com.mineinabyss.geary.serialization.dsl.builders.FormatsBuilder +import com.mineinabyss.geary.serialization.formats.Format import com.mineinabyss.geary.serialization.formats.Formats import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer -import com.mineinabyss.idofront.di.DI +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.modules.PolymorphicModuleBuilder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.SerializersModuleBuilder +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.serializerOrNull +import kotlin.reflect.KClass -val serializableComponents by DI.observe() +data class SerializableComponentsBuilder( + val world: Geary, + val serializers: ComponentSerializersBuilder = ComponentSerializersBuilder(), + val formats: FormatsBuilder = FormatsBuilder(), + var overridePersists: ComponentId? = null, + var addGearyEntitySerializer: Boolean = false, +) { + /** Adds a [SerializersModule] for polymorphic serialization of [Component]s within the ECS. */ + inline fun components(crossinline init: PolymorphicModuleBuilder.() -> Unit) { + module { polymorphic(Component::class) { init() } } + } -interface SerializableComponents { - val serializers: ComponentSerializers - val formats: Formats - val persists: ComponentId + fun format(ext: String, format: (SerializersModule) -> Format) { + formats.register(ext, format) + } - interface Builder { - val serializersBuilder: ComponentSerializersBuilder - val formatsBuilder: FormatsBuilder + /** + * Adds a serializable component and registers it with Geary to allow finding the appropriate class via + * component serial name. + */ + inline fun PolymorphicModuleBuilder.component(serializer: KSerializer) { + component(T::class, serializer) } - companion object : GearyAddonWithDefault { - override fun default(): Builder = object : Builder { - override val serializersBuilder = ComponentSerializersBuilder() - override val formatsBuilder = FormatsBuilder() + /** + * Adds a serializable component and registers it with Geary to allow finding the appropriate class via + * component serial name. + */ + @OptIn(InternalSerializationApi::class) + fun PolymorphicModuleBuilder.component( + subclass: KClass, + serializer: KSerializer = subclass.serializerOrNull() + ?: error("No serializer found for $subclass while registering serializable component"), + ) { + val serialName = serializer.descriptor.serialName + if (serializers.serialNameToClass.containsKey(serialName)) { + error("A component with serial name $serialName is already registered") } + serializers.serialNameToClass[serialName] = subclass + subclass(subclass, serializer) + } + + inline fun namedComponent(name: String) { + serializers.serialNameToClass[name] = T::class + } - override fun Builder.install() { - SerializableComponentsDSL(this).apply { - components { - component(GearyEntitySerializer) - } - } - geary.pipeline.runOnOrAfter(GearyPhase.ADDONS_CONFIGURED) { - DI.add(object : SerializableComponents { - override val serializers = serializersBuilder.build() - override val formats = formatsBuilder.build(serializers) - override val persists: ComponentId = componentId() - }) - } + /** Adds a [SerializersModule] to be used for polymorphic serialization within the ECS. */ + inline fun module(init: SerializersModuleBuilder.() -> Unit) { + serializers.modules += SerializersModule { init() } + } + + fun build(): SerializableComponentsModule { + if (addGearyEntitySerializer) { + components { component(GearyEntitySerializer(world)) } } + return SerializableComponentsModule( + serializers = serializers.build(), + formats = formats.build(serializers.build()), + persists = overridePersists ?: world.componentId() + ) } } + +data class SerializableComponentsModule( + val serializers: ComponentSerializers, + val formats: Formats, + val persists: ComponentId, +) + +val SerializableComponents = createAddon( + "Serializable Components", + { SerializableComponentsBuilder(this) } +) { configuration.build() } + +@GearyDSL +fun GearySetup.serialization(configure: SerializableComponentsBuilder.() -> Unit) = + install(SerializableComponents, configure) diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/SerializableComponentsDSL.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/SerializableComponentsDSL.kt deleted file mode 100644 index 807961b6d..000000000 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/SerializableComponentsDSL.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.mineinabyss.geary.serialization.dsl - -import com.mineinabyss.geary.addons.dsl.GearyDSL -import com.mineinabyss.geary.datatypes.Component -import com.mineinabyss.geary.modules.GearyConfiguration -import com.mineinabyss.geary.serialization.SerializableComponents -import com.mineinabyss.geary.serialization.formats.Format -import kotlinx.serialization.InternalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.SerializersModuleBuilder -import kotlinx.serialization.modules.polymorphic -import kotlinx.serialization.serializerOrNull -import kotlin.reflect.KClass - -@GearyDSL -class SerializableComponentsDSL( - builder: SerializableComponents.Builder -) { - val serializers = builder.serializersBuilder - val formats = builder.formatsBuilder - - /** Adds a [SerializersModule] for polymorphic serialization of [Component]s within the ECS. */ - inline fun components(crossinline init: PolymorphicModuleBuilder.() -> Unit) { - module { polymorphic(Component::class) { init() } } - } - - fun format(ext: String, format: (SerializersModule) -> Format) { - formats.register(ext, format) - } - - /** - * Adds a serializable component and registers it with Geary to allow finding the appropriate class via - * component serial name. - */ - inline fun PolymorphicModuleBuilder.component(serializer: KSerializer) { - component(T::class, serializer) - } - - /** - * Adds a serializable component and registers it with Geary to allow finding the appropriate class via - * component serial name. - */ - @OptIn(InternalSerializationApi::class) - fun PolymorphicModuleBuilder.component( - subclass: KClass, - serializer: KSerializer = subclass.serializerOrNull() - ?: error("No serializer found for $subclass while registering serializable component") - ) { - val serialName = serializer.descriptor.serialName - if (serializers.serialNameToClass.containsKey(serialName)) { - error("A component with serial name $serialName is already registered") - } - serializers.serialNameToClass[serialName] = subclass - subclass(subclass, serializer) - } - - inline fun namedComponent(name: String) { - serializers.serialNameToClass[name] = T::class - } - - /** Adds a [SerializersModule] to be used for polymorphic serialization within the ECS. */ - inline fun module(init: SerializersModuleBuilder.() -> Unit) { - serializers.modules += SerializersModule { init() } - } -} - -@GearyDSL -fun GearyConfiguration.serialization(configure: SerializableComponentsDSL.() -> Unit) = - install(SerializableComponents).also { SerializableComponentsDSL(it).configure() } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/WithCommonComponentNames.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/WithCommonComponentNames.kt index a52d5888a..be8dcf681 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/WithCommonComponentNames.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/WithCommonComponentNames.kt @@ -3,8 +3,9 @@ package com.mineinabyss.geary.serialization.dsl import com.mineinabyss.geary.components.relations.ChildOf import com.mineinabyss.geary.components.relations.InstanceOf import com.mineinabyss.geary.observers.events.* +import com.mineinabyss.geary.serialization.SerializableComponentsBuilder -fun SerializableComponentsDSL.withCommonComponentNames() { +fun SerializableComponentsBuilder.withCommonComponentNames() { namedComponent("geary:on_add") namedComponent("geary:on_set") namedComponent("geary:on_first_set") diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/builders/FormatsBuilder.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/builders/FormatsBuilder.kt index f9e7e5f94..2d147d21d 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/builders/FormatsBuilder.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/dsl/builders/FormatsBuilder.kt @@ -7,9 +7,9 @@ import com.mineinabyss.geary.serialization.formats.SimpleFormats import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.modules.SerializersModule -class FormatsBuilder { - val formats = mutableMapOf Format>() - +data class FormatsBuilder( + val formats: MutableMap Format> = mutableMapOf() +) { /** Registers a [Format] for a file with extension [ext]. */ fun register(ext: String, makeFromat: (SerializersModule) -> Format) { formats[ext] = makeFromat diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/helpers/Component.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/helpers/Component.kt index 6bf50c045..57d22ca0c 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/helpers/Component.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/helpers/Component.kt @@ -2,11 +2,12 @@ package com.mineinabyss.geary.serialization.helpers import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.serialization.serializableComponents +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.SerializableComponents /** * Gets the id of a component by its serial name. * Throws an error if the component name does not exist. */ -fun componentId(serialName: String): ComponentId = - componentId(serializableComponents.serializers.getClassFor(serialName)) +fun Geary.componentId(serialName: String): ComponentId = + componentId(getAddon(SerializableComponents).serializers.getClassFor(serialName)) diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt index 51eff50be..526c56711 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt @@ -2,22 +2,24 @@ package com.mineinabyss.geary.serialization.serializers import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.getAllPersisting +import kotlinx.serialization.Contextual import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -typealias SerializableGearyEntity = @Serializable(with = GearyEntitySerializer::class) GearyEntity +//TODO register contextual serializer for world +typealias SerializableGearyEntity = @Contextual GearyEntity - -object GearyEntitySerializer : KSerializer { +class GearyEntitySerializer(val world: Geary) : KSerializer { private val componentSerializer = PolymorphicListAsMapSerializer.ofComponents() override val descriptor = SerialDescriptor("geary:entity", componentSerializer.descriptor) override fun deserialize(decoder: Decoder): GearyEntity { - return entity { + return world.entity { setAll(componentSerializer.deserialize(decoder)) } } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt index 0b3187b10..51444a3f2 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt @@ -1,6 +1,7 @@ package com.mineinabyss.geary.serialization.serializers import com.mineinabyss.geary.datatypes.GearyComponent +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.serialization.ComponentSerializers.Companion.fromCamelCaseToSnakeCase import com.mineinabyss.geary.serialization.ComponentSerializers.Companion.hasNamespace @@ -57,7 +58,7 @@ open class PolymorphicListAsMapSerializer( } when (config.onMissingSerializer) { OnMissing.ERROR -> throw it - OnMissing.WARN -> geary.logger.w("No serializer found for $key in namespaces $namespaces, ignoring") + OnMissing.WARN -> Geary.w("No serializer found for $key in namespaces $namespaces, ignoring") OnMissing.IGNORE -> Unit } compositeDecoder.skipMapValue() @@ -70,7 +71,7 @@ open class PolymorphicListAsMapSerializer( config.whenComponentMalformed(key) parentConfig?.whenComponentMalformed?.invoke(key) if (config.skipMalformedComponents) { - geary.logger.w( + Geary.w( "Malformed component $key, ignoring:\n" + it.stackTraceToString() .lineSequence() @@ -96,6 +97,7 @@ open class PolymorphicListAsMapSerializer( namespaces: List, key: String, ): Result> = runCatching { + val namespaces = namespaces.plus("geary").toSet() if (key.startsWith("kotlin.")) { return@runCatching serializersModule.getPolymorphic(polymorphicSerializer.baseClass, key) as KSerializer } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt index 00de2ef14..e99a62973 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt @@ -3,7 +3,6 @@ package com.mineinabyss.geary.serialization.serializers import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.serialization.serializableComponents import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind diff --git a/addons/geary-serialization/src/jvmMain/kotlin/com/mineinabyss/geary/serialization/formats/YamlFormat.kt b/addons/geary-serialization/src/jvmMain/kotlin/com/mineinabyss/geary/serialization/formats/YamlFormat.kt index 9e4b8dfd0..2e4488521 100644 --- a/addons/geary-serialization/src/jvmMain/kotlin/com/mineinabyss/geary/serialization/formats/YamlFormat.kt +++ b/addons/geary-serialization/src/jvmMain/kotlin/com/mineinabyss/geary/serialization/formats/YamlFormat.kt @@ -1,9 +1,6 @@ package com.mineinabyss.geary.serialization.formats -import com.charleskorn.kaml.Yaml -import com.charleskorn.kaml.YamlConfiguration -import com.charleskorn.kaml.decodeFromStream -import com.charleskorn.kaml.encodeToStream +import com.charleskorn.kaml.* import com.mineinabyss.geary.serialization.formats.Format.ConfigType import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy @@ -14,22 +11,28 @@ import org.intellij.lang.annotations.Language import java.io.InputStream class YamlFormat( - module: SerializersModule + module: SerializersModule, + configuration: YamlConfiguration = YamlConfiguration( + encodeDefaults = false, + polymorphismStyle = PolymorphismStyle.Property, + polymorphismPropertyName = "type", + ), + nonStrictConfiguration: YamlConfiguration = YamlConfiguration( + encodeDefaults = false, + strictMode = false, + polymorphismStyle = PolymorphismStyle.Property, + polymorphismPropertyName = "type", + ), ) : Format { override val ext = "yml" private val nonStrictYaml = Yaml( - configuration = YamlConfiguration( - encodeDefaults = false, - strictMode = false, - ) + configuration = nonStrictConfiguration ) val regularYaml = Yaml( serializersModule = module, - configuration = YamlConfiguration( - encodeDefaults = false, - ) + configuration = configuration ) override fun decodeFromFile( diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt index 592cb5caf..40dd81e2e 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt @@ -1,33 +1,34 @@ package com.mineinabyss.geary.uuid import com.benasher44.uuid.Uuid +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.toGeary import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.synchronized interface UUID2GearyMap { - operator fun get(uuid: Uuid): GearyEntity? + operator fun get(uuid: Uuid): EntityId? - operator fun set(uuid: Uuid, entity: GearyEntity): GearyEntity? + operator fun set(uuid: Uuid, entity: EntityId): EntityId? operator fun contains(uuid: Uuid): Boolean - fun remove(uuid: Uuid): GearyEntity? + fun remove(uuid: Uuid): EntityId? } class SimpleUUID2GearyMap : UUID2GearyMap { private val map = mutableMapOf() - override operator fun get(uuid: Uuid): GearyEntity? = - map[uuid]?.toGeary() + override operator fun get(uuid: Uuid): EntityId? = + map[uuid]?.toULong() - override operator fun set(uuid: Uuid, entity: GearyEntity): GearyEntity? = - map.put(uuid, entity.id.toLong())?.toGeary() + override operator fun set(uuid: Uuid, entity: EntityId): EntityId? = + map.put(uuid, entity.toLong())?.toULong() override operator fun contains(uuid: Uuid): Boolean = map.containsKey(uuid) - override fun remove(uuid: Uuid): GearyEntity? = - map.remove(uuid)?.toGeary() + override fun remove(uuid: Uuid): EntityId? = + map.remove(uuid)?.toULong() } @@ -35,8 +36,8 @@ class SynchronizedUUID2GearyMap : UUID2GearyMap { private val unsafe = SimpleUUID2GearyMap() private val lock = SynchronizedObject() - override fun get(uuid: Uuid): GearyEntity? = synchronized(lock) { unsafe[uuid] } - override fun set(uuid: Uuid, entity: GearyEntity): GearyEntity? = synchronized(lock) { unsafe.set(uuid, entity) } + override fun get(uuid: Uuid): EntityId? = synchronized(lock) { unsafe[uuid] } + override fun set(uuid: Uuid, entity: EntityId): EntityId? = synchronized(lock) { unsafe.set(uuid, entity) } override fun contains(uuid: Uuid): Boolean = synchronized(lock) { unsafe.contains(uuid) } - override fun remove(uuid: Uuid): GearyEntity? = synchronized(lock) { unsafe.remove(uuid) } + override fun remove(uuid: Uuid): EntityId? = synchronized(lock) { unsafe.remove(uuid) } } 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 ebf5d2c9b..cb0a8fbf6 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,20 +1,13 @@ package com.mineinabyss.geary.uuid -import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.uuid.systems.untrackUuidOnRemove +import com.mineinabyss.geary.addons.dsl.createAddon import com.mineinabyss.geary.uuid.systems.trackUUIDOnAdd -import com.mineinabyss.idofront.di.DI - -val uuid2Geary by DI.observe() - -object UUIDTracking : GearyAddonWithDefault { - override fun default() = SimpleUUID2GearyMap() +import com.mineinabyss.geary.uuid.systems.untrackUuidOnRemove - override fun UUID2GearyMap.install() { - geary.run { - trackUUIDOnAdd() - untrackUuidOnRemove() - } +val UUIDTracking = createAddon("UUID Tracking", ::SimpleUUID2GearyMap) { + onStart { + trackUUIDOnAdd(configuration) + untrackUuidOnRemove(configuration) } } + 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 ac72c70a0..56c63c452 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,21 +2,20 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid import com.benasher44.uuid.uuid4 -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnSet -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query +import com.mineinabyss.geary.uuid.UUID2GearyMap import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash -import com.mineinabyss.geary.uuid.uuid2Geary -fun GearyModule.trackUUIDOnAdd() = observe().involving(query()).exec { (uuid) -> +fun Geary.trackUUIDOnAdd(uuid2Geary: UUID2GearyMap) = observe().involving(query()).exec { (uuid) -> val regenerateUUIDOnClash = entity.get() if (uuid in uuid2Geary) if (regenerateUUIDOnClash != null) { val newUuid = uuid4() entity.set(newUuid) - uuid2Geary[newUuid] = entity + uuid2Geary[newUuid] = entity.id } else error("Tried tracking entity $entity with already existing uuid $uuid") else - uuid2Geary[uuid] = entity + uuid2Geary[uuid] = entity.id } 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 d9a51f939..4b015c28f 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,12 +1,10 @@ package com.mineinabyss.geary.uuid.systems import com.benasher44.uuid.Uuid -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnRemove -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query -import com.mineinabyss.geary.uuid.uuid2Geary +import com.mineinabyss.geary.uuid.UUID2GearyMap -fun GearyModule.untrackUuidOnRemove() = observe() +fun Geary.untrackUuidOnRemove(uuid2Geary: UUID2GearyMap) = observe() .involving(query()).exec { (uuid) -> uuid2Geary.remove(uuid) } - diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/Namespaced.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/Namespaced.kt index 4a2244413..7f006556b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/Namespaced.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/Namespaced.kt @@ -1,8 +1,8 @@ package com.mineinabyss.geary.addons import com.mineinabyss.geary.addons.dsl.GearyDSL -import com.mineinabyss.geary.modules.GearyConfiguration +import com.mineinabyss.geary.modules.GearySetup @GearyDSL -class Namespaced(val namespace: String, val gearyConf: GearyConfiguration) +class Namespaced(val namespace: String, val setup: GearySetup) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index ccdb6ecb8..bc17da72a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -1,9 +1,104 @@ package com.mineinabyss.geary.addons.dsl -interface GearyAddonWithDefault: GearyAddon { +import com.mineinabyss.geary.addons.GearyPhase +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.idofront.di.DIContext +import kotlin.jvm.JvmName + +interface GearyAddonWithDefault : GearyAddon { fun default(): Module } interface GearyAddon { fun Module.install() } + +data class Addon( + val name: String, + val defaultConfiguration: Geary.() -> Configuration, + val onInstall: Geary.(Configuration) -> Instance, +) { + operator fun invoke(customConfiguration: Geary.() -> Configuration): Addon { + return copy(defaultConfiguration = customConfiguration) + } +} + +data class AddonSetup( + val name: String, + val configuration: Configuration, + val module: GearyModule, + val context: DIContext, +) { + val geary: Geary = Geary(module, context, module.logger.withTag(name)) + + /** Runs a block during [GearyPhase.INIT_COMPONENTS] */ + fun components(configure: Geary.() -> Unit) { + on(GearyPhase.INIT_COMPONENTS) { + configure(geary) + } + } + + /** Runs a block during [GearyPhase.INIT_SYSTEMS] */ + fun systems(configure: Geary.() -> Unit) { + on(GearyPhase.INIT_SYSTEMS) { + configure(geary) + } + } + + /** Runs a block during [GearyPhase.INIT_ENTITIES] */ + fun entities(configure: Geary.() -> Unit) { + on(GearyPhase.INIT_ENTITIES) { + configure(geary) + } + } + + fun onStart(run: Geary.() -> Unit) { + on(GearyPhase.ENABLE) { + run(geary) + } + } + + /** + * Allows defining actions that should run at a specific phase during startup + * + * Within its context, invoke a [GearyPhase] to run something during it, ex: + * + * ``` + * GearyLoadPhase.ENABLE { + * // run code here + * } + * ``` + */ + fun on(phase: GearyPhase, run: () -> Unit) { + module.pipeline.runOnOrAfter(phase, run) + } +} + +fun createAddon( + name: String, + init: AddonSetup.() -> Unit = {}, +): Addon = Addon(name, { }) { + init(AddonSetup(name, it, module, context)) +} + +@JvmName("createAddon1") +fun createAddon( + name: String, + configuration: Geary.() -> Conf, + init: AddonSetup.() -> Unit = {}, +): Addon = Addon(name, configuration) { conf -> + init(AddonSetup(name, conf, module, context)) + conf +} + +@JvmName("createAddon2") +fun createAddon( + name: String, + configuration: Geary.() -> Conf, + init: AddonSetup.() -> Inst, +): Addon { + return Addon(name, configuration) { + init(AddonSetup(name, it, module, context)) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt index 0b519e8e1..96869ca53 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt @@ -5,11 +5,11 @@ import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.Engine import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.ArchetypeEngineModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnAdd import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.RelationWithData -import kotlin.jvm.JvmInline import kotlin.reflect.KClass typealias GearyEntity = Entity @@ -19,41 +19,44 @@ typealias GearyEntity = Entity * * Provides some useful functions, so we aren't forced to go through [Engine] every time we want to do some things. */ -@JvmInline -value class Entity(val id: EntityId) { +//TODO add require checks for entities across different worlds +class Entity(val id: EntityId, val world: Geary) { inline val idL get() = id.toLong() - private val entityProvider get() = geary.entityProvider - private val queryManager get() = geary.queryManager - private val read get() = geary.read - private val write get() = geary.write + val geary get() = world.module + private val entityProvider get() = world.module.entityProvider + private val entityRemoveProvider get() = world.module.entityRemoveProvider + private val queryManager get() = world.module.queryManager + private val read get() = world.module.read + private val write get() = world.module.write + private val records get() = (world.module as ArchetypeEngineModule).records /** * Gets this entity's type (the ids of components added to it) * or throws an error if it is no longer active on the koinGet(). */ - val type: EntityType get() = entityProvider.getType(this) + val type: EntityType get() = records.getType(id) - val children: List - get() = queryManager.getEntitiesMatching(family { + val children: EntityArray + get() = queryManager.getEntitiesMatching(world.family { hasRelation(geary.components.childOf, this@Entity.id) - }) + }).toEntityArray(world) - val instances: List - get() = queryManager.getEntitiesMatching(family { + val instances: EntityArray + get() = queryManager.getEntitiesMatching(world.family { hasRelation(geary.components.instanceOf, this@Entity.id) - }) + }).toEntityArray(world) val prefabs: List - get() = getRelations(geary.components.instanceOf, geary.components.any).map { it.target.toGeary() } + get() = getRelations(geary.components.instanceOf, geary.components.any).map { it.target.toGeary(world) } /** Remove this entity from the ECS. */ fun removeEntity() { - entityProvider.remove(this) + entityRemoveProvider.remove(id) } /** Checks whether this entity has not been removed. */ - fun exists(): Boolean = read.exists(this) + fun exists(): Boolean = read.exists(id) /** * Sets a component that holds data for this entity @@ -63,14 +66,14 @@ value class Entity(val id: EntityId) { inline fun set( component: T, kClass: KClass = T::class, - noEvent: Boolean = false - ): Unit = set(component, componentId(kClass), noEvent) + noEvent: Boolean = false, + ): Unit = set(component, world.componentId(kClass), noEvent) fun set( component: Component, componentId: ComponentId, - noEvent: Boolean = false - ): Unit = write.setComponentFor(this, componentId, component, noEvent) + noEvent: Boolean = false, + ): Unit = write.setComponentFor(id, componentId, component, noEvent) /** Sets components that hold data for this entity */ fun setAll(components: Collection, override: Boolean = true) { @@ -85,7 +88,7 @@ value class Entity(val id: EntityId) { * @param noEvent If true, will not fire an [OnAdd] event. */ fun add(component: ComponentId, noEvent: Boolean = false) { - write.addComponentFor(this, component, noEvent) + write.addComponentFor(id, component, noEvent) } /** @@ -94,7 +97,7 @@ value class Entity(val id: EntityId) { * @param noEvent If true, will not fire an [OnAdd] event. */ inline fun add(noEvent: Boolean = false) { - add(componentId(), noEvent) + add(world.componentId(), noEvent) } /** @@ -112,15 +115,15 @@ value class Entity(val id: EntityId) { * @return Whether the component was present before removal. */ inline fun remove(noEvent: Boolean = false): Boolean = - remove(componentId(), noEvent) + remove(world.componentId(), noEvent) /** Removes a component whose class is [kClass] from this entity. */ fun remove(kClass: KClass<*>, noEvent: Boolean = false): Boolean = - remove(componentId(kClass), noEvent) + remove(world.componentId(kClass), noEvent) /** Removes a component with id [component] from this entity. */ fun remove(component: ComponentId, noEvent: Boolean = false): Boolean = - write.removeComponentFor(this, component, noEvent) + write.removeComponentFor(id, component, noEvent) /** * Removes a list of [components] from this entity. @@ -132,7 +135,7 @@ value class Entity(val id: EntityId) { /** Clears all components on this entity. */ fun clear() { - write.clearEntity(this) + write.clearEntity(id) } /** Gets a component of type [T] on this entity. */ @@ -140,20 +143,20 @@ value class Entity(val id: EntityId) { /** @see get */ inline fun get(kClass: KClass): T? = - get(componentId(kClass)) as? T + get(world.componentId(kClass)) as? T /** Gets a [component] which holds data from this entity. Use [has] if the component is not to hold data. */ fun get(component: ComponentId): Component? = - read.getComponentFor(this, component) + read.get(id, component) /** Gets a component of type [T] or sets a [default] if no component was present. */ inline fun getOrSet( kClass: KClass = T::class, - default: () -> T + default: () -> T, ): T = get(kClass) ?: default().also { set(it) } /** Gets all the components on this entity, as well as relations in the form of [RelationComponent]. */ - fun getAll(): Set = read.getComponentsFor(this).toSet() + fun getAll(): Set = read.getAll(id).toSet() /** * Checks whether this entity is an instance of another [entity] @@ -166,11 +169,11 @@ value class Entity(val id: EntityId) { /** @see has */ inline fun has(kClass: KClass): Boolean = - has(componentId(kClass)) + has(world.componentId(kClass)) /** Checks whether this entity has a [component], regardless of it holding data. */ fun has(component: ComponentId): Boolean = - read.hasComponentFor(this, component) + read.has(id, component) /** * Checks whether an entity has all of [components] set or added. @@ -181,7 +184,8 @@ value class Entity(val id: EntityId) { /** Adds a [base] entity to this entity. */ fun extend(base: Entity) { - write.extendFor(this, base) + require(base.world == world) + write.extendFor(id, base.id) } /** Adds a [prefab] entity to this entity. */ @@ -193,21 +197,21 @@ value class Entity(val id: EntityId) { /** Gets the data stored under the relation of kind [K] and target [T]. */ inline fun getRelation(): K? { - return getRelation(component()) + return getRelation(world.component()) } /** Gets the data stored under the relation of kind [K] and target [target]. */ inline fun getRelation(target: Entity): K? { - return get(Relation.of(target).id) as? K + return get(Relation.of(world, target).id) as? K } /** Like [getRelations], but reads appropriate data as requested and puts it in a [RelationWithData] object. */ @Suppress("UNCHECKED_CAST") // Intrnal logic ensures cast always succeeds inline fun getRelationsWithData(): List> = geary.read.getRelationsWithDataFor( - this, - componentIdWithNullable(), - componentIdWithNullable() + id, + world.componentIdWithNullable(), + world.componentIdWithNullable() ) as List> fun getRelationsByKind(kind: ComponentId): List = @@ -215,56 +219,56 @@ value class Entity(val id: EntityId) { /** Queries for relations using the same format as [AccessorOperations.getRelations]. */ inline fun getRelations(): List = - getRelations(componentIdWithNullable(), componentIdWithNullable()) + getRelations(world.componentIdWithNullable(), world.componentIdWithNullable()) fun getRelations(kind: ComponentId, target: EntityId): List = - read.getRelationsFor(this, kind, target) + read.getRelationsFor(id, kind, target) inline fun hasRelation(): Boolean = - hasRelation(component()) + hasRelation(world.component()) inline fun hasRelation(target: Entity): Boolean = - has(Relation.of(target).id) + has(Relation.of(world, target).id) inline fun setRelation(data: K, noEvent: Boolean = false) { - setRelation(data, component(), noEvent) + setRelation(data, world.component(), noEvent) } inline fun setRelation(data: K, target: Entity, noEvent: Boolean = false) { - setRelation(componentId(), target.id, data, noEvent) + setRelation(world.componentId(), target.id, data, noEvent) } fun setRelation(kind: ComponentId, target: EntityId, data: Component, noEvent: Boolean = false) { - geary.write.setComponentFor(this, Relation.of(kind, target).id, data, noEvent) + geary.write.setComponentFor(id, Relation.of(kind, target).id, data, noEvent) } inline fun addRelation(noEvent: Boolean = false) { - addRelation(component(), noEvent) + addRelation(world.component(), noEvent) } inline fun addRelation(target: Entity, noEvent: Boolean = false) { - geary.write.addComponentFor(this, Relation.of(target).id, noEvent) + geary.write.addComponentFor(id, Relation.of(world, target).id, noEvent) } fun addRelation(kind: ComponentId, target: EntityId, noEvent: Boolean = false) { - geary.write.addComponentFor(this, Relation.of(kind, target).id, noEvent) + geary.write.addComponentFor(id, Relation.of(kind, target).id, noEvent) } inline fun removeRelation(noEvent: Boolean = false): Boolean { - return removeRelation(component(), noEvent) + return removeRelation(world.component(), noEvent) } inline fun removeRelation(target: Entity, noEvent: Boolean = false): Boolean { - return geary.write.removeComponentFor(this, Relation.of(target).id, noEvent) + return geary.write.removeComponentFor(id, Relation.of(world, target).id, noEvent) } // Events - inline fun emit(data: T? = null, involving: ComponentId = NO_COMPONENT) { - emit(componentId(), data, involving) + inline fun emit(data: T? = null, involving: ComponentId = NO_COMPONENT) { + emit(world.componentId(), data, involving) } fun emit(event: ComponentId, data: Any? = null, involving: ComponentId = NO_COMPONENT) { - geary.eventRunner.callEvent(event, data, involving, this) + geary.eventRunner.callEvent(event, data, involving, id) } // Prefabs diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt new file mode 100644 index 000000000..bc021ce1a --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt @@ -0,0 +1,37 @@ +package com.mineinabyss.geary.datatypes + +import com.mineinabyss.geary.modules.Geary +import kotlin.jvm.JvmInline + +typealias EntityIdArray = ULongArray + +fun EntityIdArray.toEntityArray(world: Geary): EntityArray { + return EntityArray(world, this) +} + +class EntityArray( + val world: Geary, + val ids: EntityIdArray, +): Collection { + override val size: Int get() = ids.size + + override fun isEmpty(): Boolean = ids.isEmpty() + + override fun iterator(): Iterator = object : Iterator { + private var index = 0 + override fun hasNext(): Boolean = index < ids.size + override fun next(): Entity = GearyEntity(ids[index++], world) + } + + override fun containsAll(elements: Collection): Boolean { + return elements.all { contains(it) } + } + + override fun contains(element: Entity): Boolean { + return ids.contains(element.id) + } + + inline fun fastForEach(action: (Entity) -> Unit) { + for (i in ids.indices) action(GearyEntity(ids[i], world)) + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityStack.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityStack.kt index d2466551d..ddb52d8b9 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityStack.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityStack.kt @@ -1,16 +1,14 @@ package com.mineinabyss.geary.datatypes -import com.mineinabyss.geary.helpers.toGeary - class EntityStack( @PublishedApi - internal val stack: ArrayDeque = ArrayDeque() + internal val stack: ArrayDeque = ArrayDeque(), ) { - fun push(entity: Entity) { - stack.add(entity.id.toLong()) + fun push(entity: EntityId) { + stack.add(entity.toLong()) } - inline fun popOrElse(orElse: () -> Entity): Entity = + inline fun popOrElse(orElse: () -> EntityId): EntityId = if (stack.isEmpty()) orElse() - else stack.removeFirst().toGeary() + else stack.removeFirst().toULong() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityType.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityType.kt index c03cb4a61..f81a4d33b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityType.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityType.kt @@ -87,7 +87,7 @@ class EntityType private constructor( } override fun toString(): String = - inner.joinToString(", ", prefix = "[", postfix = "]") { it.readableString() } + inner.joinToString(", ", prefix = "[", postfix = "]") { it.readableString(TODO()) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt index 1992dc1a1..48a751d78 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.datatypes import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable import com.mineinabyss.geary.helpers.readableString +import com.mineinabyss.geary.modules.Geary import kotlin.jvm.JvmInline import kotlin.reflect.KClass @@ -33,7 +34,7 @@ value class Relation private constructor( override fun compareTo(other: Relation): Int = id.compareTo(other.id) - override fun toString(): String = "${kind.readableString()} to ${target.readableString()}" + override fun toString(): String = "${kind.readableString(TODO())} to ${target.readableString(TODO())}" companion object { fun of( @@ -45,14 +46,14 @@ value class Relation private constructor( or (target and RELATION_TARGET_MASK) // Add target, stripping any type roles ) - fun of(kind: KClass<*>, target: KClass<*>): Relation = - of(componentId(kind), componentId(target)) + fun of(world: Geary, kind: KClass<*>, target: KClass<*>): Relation = + of(world.componentId(kind), world.componentId(target)) - inline fun of(): Relation = - of(componentIdWithNullable(), componentId()) + inline fun of(world: Geary): Relation = + of(world.componentIdWithNullable(), world.componentId()) - inline fun of(target: Entity): Relation = - of(componentIdWithNullable(), target.id) + inline fun of(world: Geary, target: Entity): Relation = + of(world.componentIdWithNullable(), target.id) /** * Creates a relation from an id that is assumed to be valid. Use this to avoid boxing Relation because of 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 ee07ec83e..28a1755f5 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 @@ -1,36 +1,47 @@ package com.mineinabyss.geary.datatypes.family import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.helpers.componentIdWithNullable -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.engine.id +import com.mineinabyss.geary.engine.idWithNullable +import com.mineinabyss.geary.modules.Geary -inline fun family(init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { - return MutableFamily.Selector.And().apply(init) +inline fun Geary.family(init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { + return family(module.componentProvider, init) } -sealed class MutableFamily : Family { - sealed class Leaf : MutableFamily() { - class Component( - override var component: ComponentId - ) : Leaf(), Family.Leaf.Component +inline fun family(comp: ComponentProvider, init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { + return MutableFamily.Selector.And(comp).apply(init) +} + +sealed interface MutableFamily : Family { + val comp: ComponentProvider + + sealed interface Leaf : MutableFamily { + data class Component( + override val comp: ComponentProvider, + override var component: ComponentId, + ) : Leaf, Family.Leaf.Component - class AnyToTarget( + data class AnyToTarget( + override val comp: ComponentProvider, override var target: EntityId, - override val kindMustHoldData: Boolean - ) : Leaf(), Family.Leaf.AnyToTarget + override val kindMustHoldData: Boolean, + ) : Leaf, Family.Leaf.AnyToTarget - class KindToAny( + data class KindToAny( + override val comp: ComponentProvider, override var kind: ComponentId, - override val targetMustHoldData: Boolean - ) : Leaf(), Family.Leaf.KindToAny + override val targetMustHoldData: Boolean, + ) : Leaf, Family.Leaf.KindToAny } - sealed class Selector : MutableFamily(), Family.Selector { + sealed class Selector : MutableFamily, Family.Selector { class And( - and: MutableList = mutableListOf() + override val comp: ComponentProvider, + and: MutableList = mutableListOf(), ) : Selector(), Family.Selector.And { init { elements.addAll(and) @@ -40,7 +51,8 @@ sealed class MutableFamily : Family { } class AndNot( - andNot: MutableList = mutableListOf() + override val comp: ComponentProvider, + andNot: MutableList = mutableListOf(), ) : Selector(), Family.Selector.AndNot { init { elements.addAll(andNot) @@ -50,7 +62,8 @@ sealed class MutableFamily : Family { } class Or( - or: MutableList = mutableListOf() + override val comp: ComponentProvider, + or: MutableList = mutableListOf(), ) : Selector(), Family.Selector.Or { init { elements.addAll(or) @@ -81,7 +94,7 @@ sealed class MutableFamily : Family { } fun has(id: ComponentId) { - add(Leaf.Component(id)) + add(Leaf.Component(comp, id)) } fun hasSet(id: ComponentId) { @@ -93,20 +106,20 @@ sealed class MutableFamily : Family { kind: ComponentId, target: EntityId, ) { - val specificKind = kind and ENTITY_MASK != geary.components.any - val specificTarget = target and ENTITY_MASK != geary.components.any + val specificKind = kind and ENTITY_MASK != comp.types.any + val specificTarget = target and ENTITY_MASK != comp.types.any return when { specificKind && specificTarget -> has(Relation.of(kind, target).id) - specificTarget -> add(Leaf.AnyToTarget(target, kind.holdsData())) - specificKind -> add(Leaf.KindToAny(kind, target.holdsData())) + specificTarget -> add(Leaf.AnyToTarget(comp, target, kind.holdsData())) + specificKind -> add(Leaf.KindToAny(comp, kind, target.holdsData())) else -> error("Has relation check cannot be Any to Any yet.") } } - inline fun hasRelation(): Unit = hasRelation(componentIdWithNullable()) + inline fun hasRelation(): Unit = hasRelation(comp.idWithNullable()) inline fun hasRelation(target: EntityId) { - val kind = componentIdWithNullable() + val kind = comp.idWithNullable() hasRelation(kind, target) } @@ -114,22 +127,22 @@ sealed class MutableFamily : Family { inline fun or(init: Or.() -> Unit) { - add(Or().apply(init)) + add(Or(comp).apply(init)) } inline fun and(init: And.() -> Unit) { - add(And().apply(init)) + add(And(comp).apply(init)) } inline fun not(init: AndNot.() -> Unit) { - add(AndNot().apply(init)) + add(AndNot(comp).apply(init)) } inline fun has(): Unit = - has(componentId()) + has(comp.id()) inline fun hasSet(): Unit = - hasSet(componentId()) + hasSet(comp.id()) fun has(vararg componentIds: ComponentId) { has(componentIds) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/ArrayTypeMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/ArrayTypeMap.kt index 11a6dfc16..b6109bd40 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/ArrayTypeMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/ArrayTypeMap.kt @@ -2,10 +2,10 @@ package com.mineinabyss.geary.datatypes.maps import androidx.collection.mutableObjectListOf import com.mineinabyss.geary.datatypes.BucketedULongArray -import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.engine.archetypes.Archetype - open class ArrayTypeMap : TypeMap { @PublishedApi internal val archList = mutableObjectListOf() @@ -16,12 +16,12 @@ open class ArrayTypeMap : TypeMap { // We don't return nullable record to avoid boxing. // Accessing an entity that doesn't exist is indicative of a problem elsewhere and should be made obvious. - open fun getArchAndRow(entity: Entity): ULong { - return archAndRow[entity.id.toInt()] + open fun getArchAndRow(entity: EntityId): ULong { + return archAndRow[entity.toInt()] } - override fun set(entity: Entity, archetype: Archetype, row: Int) { - val id = entity.id.toInt() + override fun set(entity: EntityId, archetype: Archetype, row: Int) { + val id = entity.toInt() archAndRow[id] = (indexOrAdd(archetype).toULong() shl 32) or row.toULong() } @@ -35,19 +35,23 @@ open class ArrayTypeMap : TypeMap { } else index } - override fun remove(entity: Entity) { - val id = entity.id.toInt() + override fun remove(entity: EntityId) { + val id = entity.toInt() archAndRow[id] = 0UL } - override operator fun contains(entity: Entity): Boolean { - val id = entity.id.toInt() + override operator fun contains(entity: EntityId): Boolean { + val id = entity.toInt() return id < archAndRow.size && archAndRow[id] != 0uL } - inline fun runOn(entity: Entity, run: (archetype: Archetype, row: Int) -> T): T { + inline fun runOn(entity: EntityId, run: (archetype: Archetype, row: Int) -> T): T { val info = getArchAndRow(entity) return run(archList[(info shr 32).toInt()], info.toInt()) } + + fun getType(entity: EntityId): EntityType = runOn(entity) { archetype, _ -> + archetype.type + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt index 4810fbaf8..ef2879615 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.datatypes.maps import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.helpers.hasRelationKind import com.mineinabyss.geary.helpers.hasRelationTarget import com.mineinabyss.geary.modules.geary @@ -10,6 +11,7 @@ import com.mineinabyss.geary.modules.geary * A map of [ComponentId]s to Arrays of objects with the ability to make fast queries based on component IDs. */ internal class Family2ObjectArrayMap( + val components: Components, val getIndex: ((T) -> Int)? = null, val setIndex: ((T, Int) -> Unit)? = null ) { @@ -46,8 +48,8 @@ internal class Family2ObjectArrayMap( // See componentMap definition for relations if (id.isRelation()) { val relation = Relation.of(id) - set(Relation.of(relation.kind, geary.components.any).id) - set(Relation.of(geary.components.any, relation.target).id) + set(Relation.of(relation.kind, components.any).id) + set(Relation.of(components.any, relation.target).id) } set(id) } @@ -61,8 +63,8 @@ internal class Family2ObjectArrayMap( // See componentMap definition for relations if (id.isRelation()) { val relation = Relation.of(id) - clear(Relation.of(relation.kind, geary.components.any).id) - clear(Relation.of(geary.components.any, relation.target).id) + clear(Relation.of(relation.kind, components.any).id) + clear(Relation.of(components.any, relation.target).id) } clear(id) } @@ -120,7 +122,7 @@ internal class Family2ObjectArrayMap( is Family.Leaf.Component -> componentMap[family.component.toLong()]?.copy() ?: bitsOf() is Family.Leaf.AnyToTarget -> { // The bits for relationId in componentMap represent archetypes with any relations containing target - val relationId = Relation.of(geary.components.any, family.target).id + val relationId = Relation.of(components.any, family.target).id componentMap[relationId.toLong()]?.copy()?.apply { if (family.kindMustHoldData) forEachBit { index -> val type = elementTypes[index] @@ -132,7 +134,7 @@ internal class Family2ObjectArrayMap( is Family.Leaf.KindToAny -> { // The bits for relationId in componentMap represent archetypes with any relations containing kind - val relationId = Relation.of(family.kind, geary.components.any).id + val relationId = Relation.of(family.kind, components.any).id componentMap[relationId.toLong()]?.copy()?.apply { if (family.targetMustHoldData) forEachBit { index -> val type = elementTypes[index] diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/SynchronizedTypeMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/SynchronizedTypeMap.kt index f62e15c6a..0e67430e5 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/SynchronizedTypeMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/SynchronizedTypeMap.kt @@ -3,19 +3,20 @@ package com.mineinabyss.geary.datatypes.maps import co.touchlab.stately.concurrency.Synchronizable import co.touchlab.stately.concurrency.synchronize import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.engine.archetypes.Archetype class SynchronizedArrayTypeMap : ArrayTypeMap() { private val lock = Synchronizable() - override fun getArchAndRow(entity: Entity): ULong { + override fun getArchAndRow(entity: EntityId): ULong { return lock.synchronize { super.getArchAndRow(entity) } } - override fun set(entity: Entity, archetype: Archetype, row: Int) { + override fun set(entity: EntityId, archetype: Archetype, row: Int) { lock.synchronize { super.set(entity, archetype, row) } } - override fun remove(entity: Entity) = lock.synchronize { super.remove(entity) } - override fun contains(entity: Entity): Boolean = lock.synchronize { super.contains(entity) } + override fun remove(entity: EntityId) = lock.synchronize { super.remove(entity) } + override fun contains(entity: EntityId): Boolean = lock.synchronize { super.contains(entity) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/TypeMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/TypeMap.kt index 0ec229680..f54dc3798 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/TypeMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/TypeMap.kt @@ -1,15 +1,16 @@ package com.mineinabyss.geary.datatypes.maps import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.engine.archetypes.Archetype interface TypeMap { /** Updates the record of a given entity */ - operator fun set(entity: Entity, archetype: Archetype, row: Int) + operator fun set(entity: EntityId, archetype: Archetype, row: Int) /** Removes a record associated with an entity. */ - fun remove(entity: Entity) + fun remove(entity: EntityId) /** Checks if an entity has a record associated with it. */ - operator fun contains(entity: Entity): Boolean + operator fun contains(entity: EntityId): Boolean } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt index 66a6c81ec..549e0328c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt @@ -1,14 +1,23 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.datatypes.ComponentId -import kotlin.reflect.KClass +import com.mineinabyss.geary.datatypes.HOLDS_DATA +import com.mineinabyss.geary.datatypes.NO_ROLE +import com.mineinabyss.geary.datatypes.withRole import kotlin.reflect.KClassifier -import kotlin.reflect.KType +import kotlin.reflect.typeOf interface ComponentProvider { + val types: Components /** * Given a component's [kClass], returns its [ComponentId], or registers an entity * with a [ComponentInfo] that will represent this [kClass]'s component type. */ fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId } + +inline fun ComponentProvider.id(): ComponentId = + getOrRegisterComponentIdForClass(T::class) + +inline fun ComponentProvider.idWithNullable(): ComponentId = + id().withRole(if (typeOf().isMarkedNullable) NO_ROLE else HOLDS_DATA) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Components.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Components.kt index 6b73e5fb9..1a9836e54 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Components.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/Components.kt @@ -4,24 +4,26 @@ import com.mineinabyss.geary.components.CouldHaveChildren import com.mineinabyss.geary.components.KeepEmptyArchetype import com.mineinabyss.geary.components.relations.ChildOf import com.mineinabyss.geary.components.relations.InstanceOf -import com.mineinabyss.geary.datatypes.ComponentId -import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.observers.events.* -class Components { - val any: ComponentId = componentId() - val suppressRemoveEvent = componentId() - val couldHaveChildren = componentId() - val observer = componentId() - val onAdd = componentId() - val onSet = componentId() - val onFirstSet = componentId() - val onUpdate = componentId() - val onRemove = componentId() - val onExtend = componentId() - val onEntityRemoved = componentId() - val childOf = componentId() - val instanceOf = componentId() - val keepEmptyArchetype = componentId() +class Components( + comp: ComponentProvider, +) { + val any = comp.id() + val suppressRemoveEvent = comp.id() + val couldHaveChildren = comp.id() + val observer = comp.id() + val onAdd = comp.id() + val onSet = comp.id() + val onFirstSet = comp.id() + val onUpdate = comp.id() + val onRemove = comp.id() + val onExtend = comp.id() + val onEntityRemoved = comp.id() + val childOf = comp.id() + val instanceOf = comp.id() + val noInherit = comp.id() + val keepEmptyArchetype = comp.id() } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityMutateOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityMutateOperations.kt index 2c9ec0810..ef5e587c3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityMutateOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityMutateOperations.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId interface EntityMutateOperations { /** @@ -11,24 +12,24 @@ interface EntityMutateOperations { * @param noEvent Whether to fire an [AddedComponent] event. */ fun setComponentFor( - entity: Entity, + entity: EntityId, componentId: ComponentId, data: Component, noEvent: Boolean ) /** Adds this [componentId] to the [entity]'s type but doesn't store any data. */ - fun addComponentFor(entity: Entity, componentId: ComponentId, noEvent: Boolean) + fun addComponentFor(entity: EntityId, componentId: ComponentId, noEvent: Boolean) - fun extendFor(entity: Entity, base: Entity) + fun extendFor(entity: EntityId, base: EntityId) /** Removes a [componentId] from an [entity] and clears any data previously associated with it. */ - fun removeComponentFor(entity: Entity, componentId: ComponentId, noEvent: Boolean): Boolean + fun removeComponentFor(entity: EntityId, componentId: ComponentId, noEvent: Boolean): Boolean // To avoid breaking changes from component remove events, marked for removal @Deprecated("Use removeComponentFor(entity, componentId, noEvent) instead.") - fun removeComponentFor(entity: Entity, componentId: ComponentId): Boolean + fun removeComponentFor(entity: EntityId, componentId: ComponentId): Boolean /** Removes all components from an entity. */ - fun clearEntity(entity: Entity) + fun clearEntity(entity: EntityId) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityProvider.kt index f112f2dc6..a9467b51b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityProvider.kt @@ -1,15 +1,10 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.EntityType interface EntityProvider { /** Creates a new entity. */ - fun create(): Entity - - /** Removes an entity, freeing up its entity id for later reuse. */ - fun remove(entity: Entity) - - /** Gets an [entity]'s type */ - fun getType(entity: Entity): EntityType + fun create(): EntityId } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityReadOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityReadOperations.kt index 30c9a2a1f..f483f0b4c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityReadOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/EntityReadOperations.kt @@ -5,28 +5,28 @@ import com.mineinabyss.geary.systems.accessors.RelationWithData interface EntityReadOperations { /** Gets a [componentId]'s data from an [entity] or null if not present/the component doesn't hold any data. */ - fun getComponentFor(entity: Entity, componentId: ComponentId): Component? + fun get(entity: EntityId, componentId: ComponentId): Component? /** Gets a list of all the components [entity] has, as well as relations in the form of [RelationComponent]. */ - fun getComponentsFor(entity: Entity): Array + fun getAll(entity: EntityId): Array /** Checks whether an [entity] is still active in the engine. */ - fun exists(entity: Entity): Boolean + fun exists(entity: EntityId): Boolean /** * Gets relations in the same format as [Archetype.getRelations], but when kind/target [HOLDS_DATA], the appropriate * data is written to a [RelationWithData] object. */ fun getRelationsWithDataFor( - entity: Entity, + entity: EntityId, kind: ComponentId, target: EntityId ): List> - fun getRelationsFor(entity: Entity, kind: ComponentId, target: EntityId): List + fun getRelationsFor(entity: EntityId, kind: ComponentId, target: EntityId): List /** Checks whether an [entity] has a [componentId] */ - fun hasComponentFor(entity: Entity, componentId: ComponentId): Boolean + fun has(entity: EntityId, componentId: ComponentId): Boolean } 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 290c356fb..dc88d5311 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 @@ -7,9 +7,9 @@ import com.mineinabyss.geary.systems.System import com.mineinabyss.geary.systems.TrackedSystem import com.mineinabyss.geary.systems.query.Query -class PipelineImpl : Pipeline { - private val queryManager get() = geary.queryManager - +class PipelineImpl( + val queryManager: QueryManager +) : Pipeline { private val onSystemAdd = mutableListOf<(System<*>) -> Unit>() private val repeatingSystems: MutableSet> = mutableSetOf() 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 e5ec5d34c..60b41289a 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 @@ -1,6 +1,8 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.datatypes.EntityIdArray import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.geary.systems.query.Query @@ -9,12 +11,14 @@ interface QueryManager { fun trackQuery(query: T): CachedQuery /** Returns a list of entities matching the given family. */ - fun getEntitiesMatching(family: Family): List + fun getEntitiesMatching(family: Family): EntityIdArray /** * Returns a sequence of entities matching the given family. * This should be faster than [getEntitiesMatching] but will depend on impl. * In an archetypal engine, this gets all matching archetypes first, then maps them to entities as a sequence. */ - fun getEntitiesMatchingAsSequence(family: Family): Sequence + fun getEntitiesMatchingAsSequence(family: Family): Sequence + + fun childrenOf(parent: EntityId): EntityIdArray } 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 25ed09f51..2981f11cf 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 @@ -1,12 +1,12 @@ package com.mineinabyss.geary.engine.archetypes import androidx.collection.* -import com.mineinabyss.geary.components.relations.InstanceOf -import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.Components +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.modules.archetypes -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.observers.EventRunner import com.mineinabyss.geary.observers.events.* import com.mineinabyss.geary.systems.accessors.RelationWithData @@ -19,19 +19,16 @@ import com.mineinabyss.geary.systems.accessors.RelationWithData */ class Archetype internal constructor( val type: EntityType, - var id: Int + var id: Int, + private val write: ArchetypeMutateOperations, + private val records: ArrayTypeMap, + private val archetypeProvider: ArchetypeProvider, + private val eventRunner: EventRunner, + private val comps: Components, + private val queryManager: ArchetypeQueryManager, ) { - private val records = archetypes.records - private val archetypeProvider = archetypes.archetypeProvider - private val eventRunner = archetypes.eventRunner - private val comps get() = geary.components - - val entities: Sequence - get() { - val entities = mutableListOf() - ids.forEach { entities += it.toGeary() } - return entities.asSequence() - } + val entities: EntityIdArray + get() = ULongArray(size) { ids[id].toULong() } /** The entity ids in this archetype. Indices are the same as [componentData]'s sub-lists. */ private val ids = mutableLongListOf() @@ -74,8 +71,8 @@ class Archetype internal constructor( val size: Int get() = ids.size // ==== Helper functions ==== - fun getEntity(row: Int): Entity { - return ids[row].toGeary() + fun getEntity(row: Int): EntityId { + return ids[row].toULong() } /** @@ -156,7 +153,7 @@ class Archetype internal constructor( private fun moveOnlyAdding( oldArc: Archetype, oldRow: Int, - entity: EntityId + entity: EntityId, ) = move(entity) { for (i in 0..componentData.lastIndex) { componentData[i].add(oldArc.componentData[i][oldRow]) @@ -192,14 +189,14 @@ class Archetype internal constructor( } - internal fun createWithoutData(entity: Entity): Int { - ids.add(entity.idL) + internal fun createWithoutData(entity: EntityId): Int { + ids.add(entity.toLong()) return ids.lastIndex } internal inline fun move( entity: EntityId, - copyData: () -> Unit + copyData: () -> Unit, ): Int { if (unregistered) error("Tried adding entity to archetype that is no longer registered. Was it referenced outside of Geary?") ids.add(entity.toLong()) @@ -207,7 +204,7 @@ class Archetype internal constructor( copyData() - records[Entity(entity), this] = row + records[entity, this] = row return row } @@ -229,7 +226,7 @@ class Archetype internal constructor( row: Int, componentId: ComponentId, callEvent: Boolean, - onUpdated: (Archetype, row: Int) -> Unit + onUpdated: (Archetype, row: Int) -> Unit, ) { // if already present in this archetype, stop here since we don't need to update any data if (contains(componentId)) return @@ -262,7 +259,7 @@ class Archetype internal constructor( componentId: ComponentId, data: Component, callEvent: Boolean, - onUpdated: (Archetype, row: Int) -> Unit + onUpdated: (Archetype, row: Int) -> Unit, ) { val dataComponent = componentId.withRole(HOLDS_DATA) @@ -290,15 +287,14 @@ class Archetype internal constructor( moveTo.callComponentModifyEvent(comps.onSet, componentId, newRow) { arch, row -> arch.callComponentModifyEvent(comps.onFirstSet, componentId, row, onUpdated) } - } - else onUpdated(moveTo, newRow) + } else onUpdated(moveTo, newRow) } private inline fun callComponentModifyEvent( eventType: ComponentId, involvedComp: ComponentId, row: Int, - onComplete: (Archetype, row: Int) -> Unit = { _, _ -> } + onComplete: (Archetype, row: Int) -> Unit = { _, _ -> }, ) { val entity = getEntity(row) callComponentModifyEvent(eventType, involvedComp, row) @@ -322,11 +318,11 @@ class Archetype internal constructor( val instanceEntity = instanceArch.getEntity(instanceRow) var instanceArch = instanceArch var instanceRow = instanceRow - instanceArch.addComponent(instanceRow, Relation.of(baseEntity).id, true) { arch, row -> + instanceArch.addComponent(instanceRow, Relation.of(comps.instanceOf, baseEntity).id, true) { arch, row -> instanceArch = arch; instanceRow = row } - val noInheritComponents = getRelationsByKind(componentId()).map { Relation.of(it).target } + val noInheritComponents = getRelationsByKind(comps.noInherit).map { Relation.of(it).target } type.filter { !it.holdsData() && it !in noInheritComponents }.forEach { instanceArch.addComponent(instanceRow, it, true) { arch, row -> instanceArch = arch; instanceRow = row } } @@ -336,8 +332,10 @@ class Archetype internal constructor( instanceArch = arch; instanceRow = row } } - baseEntity.children.fastForEach { - it.addParent(instanceEntity) + queryManager.childrenOf(baseEntity).forEach { child -> + // Add instanceEntity as parent + write.addComponentFor(instanceEntity, comps.couldHaveChildren, true) + write.addComponentFor(child, Relation.of(comps.childOf, instanceEntity).id, false) } records.runOn(instanceEntity) { arch, row -> instanceArch = arch; instanceRow = row } @@ -389,7 +387,7 @@ class Archetype internal constructor( if (allowUnregister == UNKNOWN) allowUnregister = if (type.contains(comps.keepEmptyArchetype)) FALSE else TRUE if (allowUnregister == FALSE) return - archetypes.queryManager.unregisterArchetype(this) + queryManager.unregisterArchetype(this) unregistered = true componentRemoveEdges.forEach { id, archetype -> archetype.componentAddEdges.remove(id.toLong()) @@ -442,7 +440,7 @@ class Archetype internal constructor( row: Int, kind: ComponentId, target: EntityId, - relations: List + relations: List, ): List> { return relations.map { relation -> RelationWithData( @@ -466,7 +464,7 @@ class Archetype internal constructor( val replacement = ids[lastIndex] ids[row] = replacement componentData.fastForEach { it[row] = it.last() } - records[replacement.toGeary(), this@Archetype] = row + records[replacement.toULong(), this@Archetype] = row } 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 a61d5399f..814c9b1b3 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 @@ -1,9 +1,9 @@ package com.mineinabyss.geary.engine.archetypes +import co.touchlab.kermit.Logger import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.engine.* import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.TrackedSystem import com.mineinabyss.geary.systems.query.Query import kotlinx.coroutines.* @@ -18,12 +18,13 @@ import kotlin.time.Duration * * Learn more [here](https://github.com/MineInAbyss/Geary/wiki/Basic-ECS-engine-architecture). */ -open class ArchetypeEngine(override val tickDuration: Duration) : TickingEngine() { - private val pipeline get() = geary.pipeline - private val logger get() = geary.logger - +open class ArchetypeEngine( + private val pipeline: Pipeline, + private val logger: Logger, + override val tickDuration: Duration, override val coroutineContext: CoroutineContext = (CoroutineScope(Dispatchers.Default) + CoroutineName("Geary Engine")).coroutineContext +) : TickingEngine() { /** Describes how to individually tick each system */ protected open fun TrackedSystem.runSystem() { 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 32fff305d..ee594100b 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 @@ -2,19 +2,26 @@ package com.mineinabyss.geary.engine.archetypes import co.touchlab.stately.concurrency.Synchronizable import co.touchlab.stately.concurrency.synchronize -import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.datatypes.EntityIdArray import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap +import com.mineinabyss.geary.datatypes.toEntityArray +import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.QueryManager import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.fastForEach import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.geary.systems.query.Query -class ArchetypeQueryManager : QueryManager { +class ArchetypeQueryManager( + val components: ComponentProvider, +) : QueryManager { private val queries = mutableListOf>() private val archetypes = Family2ObjectArrayMap( + components = components.types, getIndex = { it.id }, setIndex = { it, index -> it.id = index } ) @@ -48,13 +55,21 @@ class ArchetypeQueryManager : QueryManager { return archetypes.match(family) } - override fun getEntitiesMatching(family: Family): List { - return getArchetypesMatching(family).flatMap(Archetype::entities) + override fun getEntitiesMatching(family: Family): EntityIdArray { + val archetypes = getArchetypesMatching(family) + // TODO avoid the list creation here, make the ULongArray directly + return archetypes.flatMap(Archetype::entities).toULongArray() } - override fun getEntitiesMatchingAsSequence(family: Family): Sequence { + override fun getEntitiesMatchingAsSequence(family: Family): Sequence { return getArchetypesMatching(family) .asSequence() .flatMap(Archetype::entities) } + + override fun childrenOf(parent: EntityId): EntityIdArray { + return getEntitiesMatching(family(components) { + hasRelation(this@ArchetypeQueryManager.components.types.childOf, parent) + }) + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt index 670819a69..e9d721bf6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt @@ -1,17 +1,21 @@ package com.mineinabyss.geary.engine.archetypes +import co.touchlab.kermit.Logger import co.touchlab.stately.concurrency.Synchronizable import co.touchlab.stately.concurrency.synchronize import com.mineinabyss.geary.components.ComponentInfo import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.engine.ComponentProvider -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.engine.Components +import com.mineinabyss.geary.engine.EntityProvider +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations import kotlin.reflect.KClassifier -class ComponentAsEntityProvider : ComponentProvider { - private val entityProvider get() = geary.entityProvider - private val logger get() = geary.logger - +class ComponentAsEntityProvider( + val entityProvider: EntityProvider, + val logger: Logger, +) : ComponentProvider { + override val types: Components = Components(this) private val classToComponentMap = mutableMapOf() private val classToComponentMapLock = Synchronizable() @@ -19,8 +23,9 @@ class ComponentAsEntityProvider : ComponentProvider { logger.v("Registering ComponentInfo component") //Register an entity for the ComponentInfo component, otherwise getComponentIdForClass does a StackOverflow val componentInfo = entityProvider.create() - classToComponentMap[ComponentInfo::class] = componentInfo.id.toLong() - componentInfo.set(ComponentInfo(ComponentInfo::class), noEvent = true) + classToComponentMap[ComponentInfo::class] = componentInfo.toLong() + //TODO is this actually necessary? +// componentInfo.set(ComponentInfo(ComponentInfo::class), noEvent = true) } override fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId = @@ -34,8 +39,8 @@ class ComponentAsEntityProvider : ComponentProvider { private fun registerComponentIdForClass(kClass: KClassifier): ComponentId { logger.v("Registering new component: $kClass") val compEntity = entityProvider.create() - compEntity.set(ComponentInfo(kClass), noEvent = true) - classToComponentMap[kClass] = compEntity.id.toLong() - return compEntity.id +// compEntity.set(ComponentInfo(kClass), noEvent = true) + classToComponentMap[kClass] = compEntity.toLong() + return compEntity } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt index 3cd546e42..fcbf88145 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt @@ -1,52 +1,44 @@ package com.mineinabyss.geary.engine.archetypes import co.touchlab.stately.concurrency.AtomicLong -import com.mineinabyss.geary.datatypes.Entity -import com.mineinabyss.geary.datatypes.EntityStack -import com.mineinabyss.geary.datatypes.EntityType -import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityProvider +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.observers.EventRunner import com.mineinabyss.geary.observers.events.OnEntityRemoved -class EntityByArchetypeProvider( - private val reuseIDsAfterRemoval: Boolean = true, -) : EntityProvider { - private lateinit var records: ArrayTypeMap - - // private val archetypeProvider: ArchetypeProvider by lazy { archetypes.archetypeProvider } - private lateinit var root: Archetype - - private val removedEntities: EntityStack = EntityStack() - private val currId = AtomicLong(0L) - - override fun create(): GearyEntity { - val entity: GearyEntity = if (reuseIDsAfterRemoval) { - removedEntities.popOrElse { (currId.incrementAndGet() - 1).toGeary() } - } else (currId.incrementAndGet() - 1).toGeary() - - createRecord(entity) - return entity - } - - override fun remove(entity: Entity) { - if (!entity.has(geary.components.suppressRemoveEvent)) - entity.emit(geary.components.onEntityRemoved, OnEntityRemoved(), NO_COMPONENT) +class EntityRemove( + private val removedEntities: EntityStack, + private val reader: ArchetypeReadOperations, + private val write: ArchetypeMutateOperations, + private val records: ArrayTypeMap, + private val components: Components, + private val eventRunner: EventRunner, + private val queryManager: ArchetypeQueryManager, +){ + /** Removes an entity, freeing up its entity id for later reuse. */ + fun remove(entity: EntityId) { + if (!reader.has(entity, components.suppressRemoveEvent)) + eventRunner.callEvent(components.onEntityRemoved, null, NO_COMPONENT, entity) // remove all children of this entity from the ECS as well - if (entity.has(geary.components.couldHaveChildren)) entity.apply { - children.fastForEach { + if (reader.has(entity, components.couldHaveChildren)) entity.apply { + queryManager.childrenOf(entity).forEach { + val parents = reader.parentsOf(it) // Remove self from the child's parents or remove the child if it no longer has parents - if (it.parents == setOf(this)) it.removeEntity() - else it.removeParent(this) + if (parents == ulongArrayOf(this)) remove(it) + else write.removeComponentFor(it, Relation.of(components.childOf, this).id, false) } } // 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.getType(entity).forEach { compId -> + if (reader.has(entity, compId)) + eventRunner.callEvent(components.onRemove, null, compId, entity) } records.runOn(entity) { archetype, row -> @@ -55,17 +47,34 @@ class EntityByArchetypeProvider( removedEntities.push(entity) } } +} + +class EntityByArchetypeProvider( + private val reuseIDsAfterRemoval: Boolean = true, +) : EntityProvider { + private lateinit var records: ArrayTypeMap + + // private val archetypeProvider: ArchetypeProvider by lazy { archetypes.archetypeProvider } + private lateinit var root: Archetype + + internal val removedEntities: EntityStack = EntityStack() + private val currId = AtomicLong(0L) + + override fun create(): EntityId { + val entity: EntityId = if (reuseIDsAfterRemoval) { + removedEntities.popOrElse { (currId.incrementAndGet() - 1).toULong() } + } else (currId.incrementAndGet() - 1).toULong() + + createRecord(entity) + return entity + } fun init(records: ArrayTypeMap, root: Archetype) { this.records = records this.root = root } - override fun getType(entity: Entity): EntityType = records.runOn(entity) { archetype, _ -> - archetype.type - } - - private fun createRecord(entity: Entity) { + private fun createRecord(entity: EntityId) { val root = root val row = root.createWithoutData(entity) records[entity, root] = row diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt index 28960e6a8..1e27b6243 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt @@ -6,13 +6,20 @@ import co.touchlab.stately.concurrency.Synchronizable import co.touchlab.stately.concurrency.synchronize import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityType -import com.mineinabyss.geary.modules.archetypes - -class SimpleArchetypeProvider : ArchetypeProvider { - private val queryManager: ArchetypeQueryManager get() = archetypes.queryManager - +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.Components +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations +import com.mineinabyss.geary.observers.EventRunner + +class SimpleArchetypeProvider( + private val records: ArrayTypeMap, + private val write: ArchetypeMutateOperations, + private val eventRunner: EventRunner, + private val components: Components, + private val queryManager: ArchetypeQueryManager, +) : ArchetypeProvider { override val rootArchetype: Archetype by lazy { - Archetype(EntityType(), 0).also { + createArchetype(EntityType(), 0).also { queryManager.registerArchetype(it) } } @@ -21,14 +28,26 @@ class SimpleArchetypeProvider : ArchetypeProvider { private fun createArchetype(prevNode: Archetype, componentEdge: ComponentId): Archetype { - val arc = Archetype(prevNode.type.plus(componentEdge), queryManager.archetypeCount) - + val arc = createArchetype(prevNode.type.plus(componentEdge), queryManager.archetypeCount) arc.componentRemoveEdges[componentEdge.toLong()] = prevNode prevNode.componentAddEdges[componentEdge.toLong()] = arc queryManager.registerArchetype(arc) return arc } + private fun createArchetype(type: EntityType, id: Int): Archetype { + return Archetype( + type = type, + id = id, + write = write, + records = records, + archetypeProvider = this, + eventRunner = eventRunner, + comps = components, + queryManager = queryManager + ) + } + override fun getArchetype(entityType: EntityType): Archetype = archetypeWriteLock.synchronize { var node = rootArchetype entityType.forEach { compId -> diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt index d5d5025de..f6df3ec66 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt @@ -2,19 +2,32 @@ package com.mineinabyss.geary.engine.archetypes.operations import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityMutateOperations import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider -import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.engine.archetypes.ArchetypeQueryManager +import com.mineinabyss.geary.engine.archetypes.SimpleArchetypeProvider +import com.mineinabyss.geary.observers.EventRunner -class ArchetypeMutateOperations : EntityMutateOperations { - private lateinit var records: ArrayTypeMap - private val archetypeProvider: ArchetypeProvider get() = archetypes.archetypeProvider +class ArchetypeMutateOperations( + private val records: ArrayTypeMap, + eventRunner: EventRunner, + components: Components, + queryManager: ArchetypeQueryManager, +) : EntityMutateOperations { + val archetypeProvider: ArchetypeProvider = SimpleArchetypeProvider( + records = records, + write = this, + eventRunner = eventRunner, + components = components, + queryManager = queryManager, + ) override fun setComponentFor( - entity: Entity, + entity: EntityId, componentId: ComponentId, data: Component, - noEvent: Boolean + noEvent: Boolean, ) { records.runOn(entity) { archetype, row -> // Only add HOLDS_DATA if this isn't a relation. All relations implicitly hold data currently and that bit @@ -25,16 +38,16 @@ class ArchetypeMutateOperations : EntityMutateOperations { } override fun addComponentFor( - entity: Entity, + entity: EntityId, componentId: ComponentId, - noEvent: Boolean + noEvent: Boolean, ) { records.runOn(entity) { archetype, row -> archetype.addComponent(row, componentId.withoutRole(HOLDS_DATA), !noEvent) } } - override fun extendFor(entity: Entity, base: Entity) { + override fun extendFor(entity: EntityId, base: EntityId) { records.runOn(base) { archetype, row -> records.runOn(entity) { entityArch, entityRow -> archetype.instantiateTo(row, entityArch, entityRow) @@ -42,7 +55,7 @@ class ArchetypeMutateOperations : EntityMutateOperations { } } - override fun removeComponentFor(entity: Entity, componentId: ComponentId, noEvent: Boolean): Boolean { + override fun removeComponentFor(entity: EntityId, componentId: ComponentId, noEvent: Boolean): Boolean { val a = records.runOn(entity) { archetype, row -> archetype.removeComponent(row, componentId.withRole(HOLDS_DATA), !noEvent) } @@ -52,18 +65,15 @@ class ArchetypeMutateOperations : EntityMutateOperations { return a || b // return whether anything was changed } - override fun removeComponentFor(entity: Entity, componentId: ComponentId): Boolean = + @Deprecated("Use removeComponentFor(entity, componentId, noEvent) instead.") + override fun removeComponentFor(entity: EntityId, componentId: ComponentId): Boolean = removeComponentFor(entity, componentId, false) - override fun clearEntity(entity: Entity) { + override fun clearEntity(entity: EntityId) { records.runOn(entity) { archetype, row -> archetype.removeEntity(row) val newRow = archetypeProvider.rootArchetype.createWithoutData(entity) - records[entity, archetypes.archetypeProvider.rootArchetype] = newRow + records[entity, archetypeProvider.rootArchetype] = newRow } } - - fun init(records: ArrayTypeMap) { - this.records = records - } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt index 0c4dc5876..10e13e7d9 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt @@ -1,20 +1,25 @@ package com.mineinabyss.geary.engine.archetypes.operations import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.ComponentProvider +import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityReadOperations -import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.engine.id +import com.mineinabyss.geary.helpers.toGeary import com.mineinabyss.geary.systems.accessors.RelationWithData -class ArchetypeReadOperations : EntityReadOperations { - private val records get() = archetypes.records - - override fun getComponentFor(entity: Entity, componentId: ComponentId): Component? { +class ArchetypeReadOperations( + val components: Components, + val records: ArrayTypeMap, +) : EntityReadOperations { + override fun get(entity: EntityId, componentId: ComponentId): Component? { records.runOn(entity) { archetype, row -> return archetype[row, componentId.let { if (it.hasRole(RELATION)) it else it.withRole(HOLDS_DATA) }] } } - override fun getComponentsFor(entity: Entity): Array { + override fun getAll(entity: EntityId): Array { records.runOn(entity) { archetype, row -> return archetype.getComponents(row).also { array -> archetype.relationsWithData.forEach { relation -> @@ -25,21 +30,28 @@ class ArchetypeReadOperations : EntityReadOperations { } } - override fun exists(entity: Entity): Boolean { + override fun exists(entity: EntityId): Boolean { return records.contains(entity) } override fun getRelationsWithDataFor( - entity: Entity, + entity: EntityId, kind: ComponentId, target: EntityId, ): List> = records.runOn(entity) { archetype, row -> return archetype.readRelationDataFor(row, kind, target, archetype.getRelations(kind, target)) } - override fun getRelationsFor(entity: Entity, kind: ComponentId, target: EntityId): List = + override fun getRelationsFor(entity: EntityId, kind: ComponentId, target: EntityId): List = records.runOn(entity) { archetype, _ -> archetype.getRelations(kind, target) } - override fun hasComponentFor(entity: Entity, componentId: ComponentId): Boolean = + override fun has(entity: EntityId, componentId: ComponentId): Boolean = records.runOn(entity) { archetype, _ -> componentId in archetype } + + fun parentsOf(entity: EntityId): EntityIdArray { + return getRelationsFor(entity, components.childOf, components.any) + .map { it.target } + .toSet() + .toULongArray() + } } 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 fcb8d500c..a713dfff4 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 @@ -2,7 +2,7 @@ package com.mineinabyss.geary.helpers import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.modules.ArchetypeEngineModule -fun EntityType.getArchetype(): Archetype = - archetypes.archetypeProvider.getArchetype(this) +fun EntityType.getArchetype(world: ArchetypeEngineModule): Archetype = + world.write.archetypeProvider.getArchetype(this) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt index 55cff9d1b..d0c6f9be2 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt @@ -1,9 +1,10 @@ package com.mineinabyss.geary.helpers import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.modules.Geary import kotlin.reflect.KClass -fun EntityId.readableString(): String = buildString { +fun EntityId.readableString(world: Geary): String = buildString { val id = this@readableString if (id.hasRole(RELATION)) { append(id.toRelation().toString()) @@ -12,7 +13,7 @@ fun EntityId.readableString(): String = buildString { if (id.hasRole(RELATION)) append("R") else append('-') if (id.hasRole(HOLDS_DATA)) append("D") else append('-') append(" ") - val componentName = (id.getComponentInfo()?.kClass as? KClass<*>)?.simpleName + val componentName = (id.getComponentInfo(world)?.kClass as? KClass<*>)?.simpleName if (componentName == null) append(id and ENTITY_MASK) else append(componentName) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt index 08bc03dbe..704c4269a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt @@ -2,24 +2,24 @@ package com.mineinabyss.geary.helpers import com.mineinabyss.geary.components.ComponentInfo import com.mineinabyss.geary.datatypes.* -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.Geary import kotlin.reflect.KClass import kotlin.reflect.KClassifier import kotlin.reflect.KType import kotlin.reflect.typeOf /** Creates a new empty entity. May reuse recently deleted entity ids. */ -fun entity(): Entity = geary.entityProvider.create() +fun Geary.entity(): Entity = Entity(module.entityProvider.create(), this) /** @see entity */ -inline fun entity(run: Entity.() -> Unit): Entity = entity().apply(run) +inline fun Geary.entity(run: Entity.() -> Unit): Entity = entity().apply(run) /** Creates a new empty entity that will get removed once [run] completes or fails. */ -inline fun temporaryEntity( - run: (Entity) -> T +inline fun Geary.temporaryEntity( + run: (Entity) -> T, ): T { val entity = entity { - add(geary.components.suppressRemoveEvent, noEvent = true) + add(module.components.suppressRemoveEvent, noEvent = true) } return try { run(entity) @@ -28,24 +28,24 @@ inline fun temporaryEntity( } } -inline fun component(): Entity = component(T::class) +inline fun Geary.component(): Entity = component(T::class) -fun component(kClass: KClass<*>): Entity = componentId(kClass).toGeary() +fun Geary.component(kClass: KClass<*>): Entity = componentId(kClass).toGeary(this) /** Gets or registers the id of a component of type [T] */ -inline fun componentId(): ComponentId = componentId(T::class) +inline fun Geary.componentId(): ComponentId = componentId(T::class) /** Gets or registers the id of a component of type [T], adding the [HOLDS_DATA] role if [T] is not nullable. */ -inline fun componentIdWithNullable(): ComponentId = +inline fun Geary.componentIdWithNullable(): ComponentId = componentId().withRole(if (typeOf().isMarkedNullable) NO_ROLE else HOLDS_DATA) /** Gets or registers the id of a component by its [kType]. */ -fun componentId(kType: KType): ComponentId = +fun Geary.componentId(kType: KType): ComponentId = componentId(kType.classifier ?: error("No classifier found for type $kType")) /** Gets or registers the id of a component by its [kClass]. */ -fun componentId(kClass: KClassifier): ComponentId = - geary.componentProvider.getOrRegisterComponentIdForClass(kClass) +fun Geary.componentId(kClass: KClassifier): ComponentId = + module.componentProvider.getOrRegisterComponentIdForClass(kClass) @Deprecated("Should not be getting an id for an id!", ReplaceWith("componentId(component)")) @@ -54,7 +54,6 @@ fun componentId(kClass: KClass): Nothing = error("Trying to access id for component id") /** Gets the [ComponentInfo] component from a component's id. */ -fun ComponentId.getComponentInfo(): ComponentInfo? = - this.toGeary().get() +fun ComponentId.getComponentInfo(world: Geary): ComponentInfo? = this.toGeary(world).get() -inline fun cId(): ComponentId = componentId() +inline fun Geary.cId(): ComponentId = componentId() diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt index cf4abf3ee..4dfd3378f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt @@ -4,16 +4,17 @@ import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.ENTITY_MASK import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.modules.Geary /** Gets the entity associated with this [EntityId], stripping it of any roles, and runs code on it. */ -inline fun EntityId.toGeary(run: Entity.() -> Unit): Entity = toGeary().apply(run) +inline fun EntityId.toGeary(world: Geary, run: Entity.() -> Unit): Entity = toGeary(world).apply(run) /** Gets the entity associated with this [EntityId], stripping it of any roles. */ -fun EntityId.toGeary(): Entity = Entity(this and ENTITY_MASK) +fun EntityId.toGeary(world: Geary): Entity = Entity(this and ENTITY_MASK, world) /** Gets the entity associated with this [Long]. */ -fun Long.toGeary(): Entity = Entity(toULong() and ENTITY_MASK) +fun Long.toGeary(world: Geary): Entity = Entity(toULong() and ENTITY_MASK, world) -val NO_ENTITY: Entity = 0L.toGeary() +val Geary.NO_ENTITY: Entity get() = 0L.toGeary(this) const val NO_COMPONENT: ComponentId = 0uL diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt index f5b7f140c..e535c5f59 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt @@ -47,7 +47,7 @@ fun Entity.clearChildren() { /** Gets the first parent of this entity */ val Entity.parent: Entity? - get() = getRelations().firstOrNull()?.target?.toGeary() + get() = getRelations().firstOrNull()?.target?.toGeary(world) /** Runs code on the first parent of this entity. */ inline fun Entity.onParent( @@ -59,4 +59,4 @@ inline fun Entity.onParent( } val Entity.parents: Set - get() = getRelations().mapTo(mutableSetOf()) { it.target.toGeary() } + get() = getRelations().mapTo(mutableSetOf()) { it.target.toGeary(world) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/WithOperator.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/WithOperator.kt index 0cb5e1e2a..a6a2b59ff 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/WithOperator.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/WithOperator.kt @@ -6,7 +6,7 @@ import com.mineinabyss.geary.datatypes.Entity as GE /** Gets a component of type [T] from an entity, returning null if [T] is nullable, or an error otherwise. */ inline fun GE.nullOrError(): T { - val data = get(componentId()) as? T? + val data = get(world.componentId()) as? T? if (!typeOf().isMarkedNullable && data == null) { error("") } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt index a2941d647..f462437c4 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt @@ -1,7 +1,6 @@ package com.mineinabyss.geary.modules import co.touchlab.kermit.Logger -import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.PipelineImpl @@ -13,29 +12,36 @@ import com.mineinabyss.idofront.di.DI import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -val archetypes: ArchetypeEngineModule by DI.observe() - -open class ArchetypeEngineModule( +class ArchetypeEngineModule( tickDuration: Duration = 50.milliseconds, ) : GearyModule { + val records: ArrayTypeMap = ArrayTypeMap() override val logger = Logger.withTag("Geary") - override val queryManager = ArchetypeQueryManager() - - override val engine = ArchetypeEngine(tickDuration) - override val eventRunner = ArchetypeEventRunner() - override val pipeline = PipelineImpl() - - open val records: ArrayTypeMap = ArrayTypeMap() - - override val read = ArchetypeReadOperations() - override val write = ArchetypeMutateOperations() override val entityProvider = EntityByArchetypeProvider() - override val componentProvider = ComponentAsEntityProvider() override val defaults: Defaults = Defaults() - open val archetypeProvider: ArchetypeProvider = SimpleArchetypeProvider() - - override val components by lazy { Components() } + override val componentProvider = ComponentAsEntityProvider(entityProvider, logger) + override val components = Components(componentProvider) + override val queryManager = ArchetypeQueryManager(componentProvider) + override val read = ArchetypeReadOperations(components, records) + override val pipeline = PipelineImpl(queryManager) + override val engine = ArchetypeEngine(pipeline, logger, tickDuration) + override val eventRunner = ArchetypeEventRunner(read, components.observer, componentProvider, records) + override val write = ArchetypeMutateOperations( + records, + eventRunner = eventRunner, + components = components, + queryManager = queryManager, + ) + override val entityRemoveProvider = EntityRemove( + removedEntities = entityProvider.removedEntities, + reader = read, + write = write, + records = records, + components = components, + eventRunner = eventRunner, + queryManager = queryManager, + ) companion object : GearyModuleProviderWithDefault { override fun default(): ArchetypeEngineModule { @@ -45,18 +51,18 @@ open class ArchetypeEngineModule( override fun init(module: ArchetypeEngineModule) { DI.add(module) DI.add(module) - module.entityProvider.init(module.records, module.archetypeProvider.rootArchetype) - module.write.init(module.records) + module.entityProvider.init(module.records, module.write.archetypeProvider.rootArchetype) module.componentProvider.createComponentInfo() } override fun start(module: ArchetypeEngineModule) { - module { - on(GearyPhase.ENABLE) { - module.engine.start() - } - } - geary.pipeline.runStartupTasks() + TODO("Fix") +// module { +// on(GearyPhase.ENABLE) { +// module.engine.start() +// } +// } +// geary.pipeline.runStartupTasks() } } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt new file mode 100644 index 000000000..67d75a4d7 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -0,0 +1,81 @@ +package com.mineinabyss.geary.modules + +import co.touchlab.kermit.Logger +import co.touchlab.kermit.mutableLoggerConfigInit +import co.touchlab.kermit.platformLogWriter +import com.mineinabyss.geary.addons.dsl.Addon +import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.observers.builders.ObserverWithData +import com.mineinabyss.geary.observers.builders.ObserverWithoutData +import com.mineinabyss.geary.systems.builders.SystemBuilder +import com.mineinabyss.geary.systems.query.CachedQuery +import com.mineinabyss.geary.systems.query.Query +import com.mineinabyss.idofront.di.DIContext +import kotlin.jvm.JvmName + +/** + * Root class for users to access geary functionality. + * + * Anything exposed to the user should be accessible here without + * having to call functions on classes in the module, + * it simply acts as a container for all dependencies. + * + * Any functions that modify the state of the engine modify it right away, + * they are not scheduled for load phases like [GearySetup] is. + */ +open class Geary( + val module: GearyModule, + val context: DIContext, + val logger: Logger = module.logger, +) { + @JvmName("getAddon1") + fun , Inst> getAddon(addon: T): Inst = TODO() + @JvmName("getAddon2") + fun , Inst> getAddon(addon: T?): Inst? = TODO() + + // Queries + + fun cache( + query: T, + ): CachedQuery { + return module.queryManager.trackQuery(query) + } + + // TODO simple api for running queries in place without caching + inline fun execute( + query: T, + run: T.() -> Unit = {}, + ): CachedQuery { + TODO() + } + + inline fun observe(): ObserverWithoutData { + return ObserverWithoutData(listOf(componentId()), world = this) { + module.eventRunner.addObserver(it) + } + } + + inline fun observeWithData(): ObserverWithData { + return ObserverWithData(listOf(componentId()), world = this) { + module.eventRunner.addObserver(it) + } + } + + fun system( + query: T, + ): SystemBuilder { + val defaultName = Throwable().stackTraceToString() + .lineSequence() + .drop(2) // First line error, second line is this function + .first() + .trim() + .substringBeforeLast("(") + .substringAfter("$") + .substringAfter("Kt.") + .substringAfter("create") + + return SystemBuilder(defaultName, query, module.pipeline) + } + + companion object : Logger(mutableLoggerConfigInit(listOf(platformLogWriter())), "Geary") +} 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 b7001c604..0a3cd3857 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 @@ -8,64 +8,63 @@ import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.idofront.di.DI import kotlin.reflect.KClass -@GearyDSL -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 - return install(addon, addon.default()) - } - - inline fun , reified Module : Any> install( - addon: T, - module: Module, - ): Module = with(addon) { - DI.add(module) - module.install() - }.let { module } - - fun namespace(namespace: String, configure: Namespaced.() -> Unit) { - Namespaced(namespace, this).configure() - } - - /** Runs a block during [GearyPhase.INIT_COMPONENTS] */ - fun components(configure: GearyModule.() -> Unit) { - on(GearyPhase.INIT_COMPONENTS) { - module.configure() - } - } - - /** Runs a block during [GearyPhase.INIT_SYSTEMS] */ - fun systems(configure: GearyModule.() -> Unit) { - on(GearyPhase.INIT_SYSTEMS) { - module.configure() - } - } - - /** Runs a block during [GearyPhase.INIT_ENTITIES] */ - fun entities(configure: GearyModule.() -> Unit) { - on(GearyPhase.INIT_ENTITIES) { - module.configure() - } - } - - /** - * Allows defining actions that should run at a specific phase during startup - * - * Within its context, invoke a [GearyPhase] to run something during it, ex: - * - * ``` - * GearyLoadPhase.ENABLE { - * // run code here - * } - * ``` - */ - fun on(phase: GearyPhase, run: () -> Unit) { - geary.pipeline.runOnOrAfter(phase, run) - } -} +//@GearyDSL +//class GearyConfiguration( +// val module: GearyModule +//) { +// inline fun , reified Module : Any> install( +// addon: T, +// ): Module { +// val module = DI.getOrNull() +// if (module != null) return module +// return install(addon, addon.default()) +// } +// +// inline fun , reified Module : Any> install( +// addon: T, +// module: Module, +// ): Module = with(addon) { +// DI.add(module) +// module.install() +// }.let { module } +// +// fun namespace(namespace: String, configure: Namespaced.() -> Unit) { +// Namespaced(namespace, this).configure() +// } +// +// /** Runs a block during [GearyPhase.INIT_COMPONENTS] */ +// fun components(configure: Geary.() -> Unit) { +// on(GearyPhase.INIT_COMPONENTS) { +// module.configure() +// } +// } +// +// /** Runs a block during [GearyPhase.INIT_SYSTEMS] */ +// fun systems(configure: Geary.() -> Unit) { +// on(GearyPhase.INIT_SYSTEMS) { +// module.configure() +// } +// } +// +// /** Runs a block during [GearyPhase.INIT_ENTITIES] */ +// fun entities(configure: Geary.() -> Unit) { +// on(GearyPhase.INIT_ENTITIES) { +// module.configure() +// } +// } +// +// /** +// * Allows defining actions that should run at a specific phase during startup +// * +// * Within its context, invoke a [GearyPhase] to run something during it, ex: +// * +// * ``` +// * GearyLoadPhase.ENABLE { +// * // run code here +// * } +// * ``` +// */ +// fun on(phase: GearyPhase, run: () -> Unit) { +// geary.pipeline.runOnOrAfter(phase, run) +// } +//} 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 042b772f9..a91b7137a 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 @@ -1,35 +1,58 @@ package com.mineinabyss.geary.modules import co.touchlab.kermit.Logger -import com.mineinabyss.geary.addons.dsl.GearyDSL +import com.mineinabyss.geary.addons.dsl.* import com.mineinabyss.geary.engine.* +import com.mineinabyss.geary.engine.archetypes.EntityRemove import com.mineinabyss.geary.observers.EventRunner import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.di.DIContext -val geary: GearyModule by DI.observe() +//val geary: GearyAPI by DI.observe() fun geary( moduleProvider: GearyModuleProviderWithDefault, - configuration: GearyConfiguration.() -> Unit = {} -) { + context: DIContext = DI, + configure: GearySetup.() -> Unit = {}, +): UninitializedGearyModule { val module = moduleProvider.default() - geary(moduleProvider, module, configuration) + return geary(moduleProvider, module, context, configure) } fun geary( moduleProvider: GearyModuleProvider, module: T, - configuration: GearyConfiguration.() -> Unit = {} -) { + context: DIContext = DI, + configure: GearySetup.() -> Unit = {}, +): UninitializedGearyModule { moduleProvider.init(module) - module.invoke(configuration) - moduleProvider.start(module) + val setup = GearySetup(module, context) + configure(setup) + return UninitializedGearyModule(setup, moduleProvider as GearyModuleProvider) +} + +data class UninitializedGearyModule( + val setup: GearySetup, + val provider: GearyModuleProvider +) { + inline fun configure(configure: GearySetup.() -> Unit) = setup.configure() + + fun start(): Geary{ + setup.addons.initAll(setup) + provider.start(setup.module) + setup.module.pipeline.runStartupTasks() // TODO keep pipeline separate, it shouldnt be used after init + return Geary(setup.module, setup.context) + } } +/** + * Describes all the dependencies needed to initialize and run a Geary engine. + */ @GearyDSL interface GearyModule { val logger: Logger val entityProvider: EntityProvider + val entityRemoveProvider: EntityRemove val componentProvider: ComponentProvider val read: EntityReadOperations @@ -43,11 +66,5 @@ interface GearyModule { val pipeline: Pipeline val defaults: Defaults - - operator fun invoke(configure: GearyConfiguration.() -> Unit) { - GearyConfiguration(this).apply(configure) - } - - companion object } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt new file mode 100644 index 000000000..a0b9e3938 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt @@ -0,0 +1,27 @@ +package com.mineinabyss.geary.modules + +import com.mineinabyss.geary.addons.Namespaced +import com.mineinabyss.geary.addons.dsl.Addon +import com.mineinabyss.idofront.di.DIContext + +/** + * Represents a Geary engine whose dependencies have been created in a [GearyModule] and is ready to have addons + * installed. Load phases are accessible here and will be called once start gets called. + */ +class GearySetup( + val module: GearyModule, + val context: DIContext, +) { + val addons = MutableAddons() + val logger = module.logger + val geary = Geary(module, context, logger) + + inline fun , Conf> install(addon: T, configure: Conf.() -> Unit = {}): T { + addons.getOrPut(geary, addon).apply { config.configure() } + return addon + } + + fun namespace(namespace: String, configure: Namespaced.() -> Unit) { + Namespaced(namespace, this).configure() + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt new file mode 100644 index 000000000..96e66cd15 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt @@ -0,0 +1,23 @@ +package com.mineinabyss.geary.modules + +import com.mineinabyss.geary.addons.dsl.Addon + +class MutableAddons { + data class AddonToConfig(val addon: Addon, val config: T) + + @PublishedApi + internal val addons = mutableMapOf>() + + fun getOrPut(world: Geary, addon: Addon, customConfiguration: (() -> T)? = null): AddonToConfig { + return addons.getOrPut(addon.name) { + AddonToConfig(addon, customConfiguration?.invoke() ?: addon.defaultConfiguration(world)) + } as AddonToConfig + } + + fun initAll(setup: GearySetup) { + addons.forEach { (name, addon) -> + val geary = Geary(setup.module, setup.context, setup.module.logger.withTag(name)) + (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) + } + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt index bc05055a8..3a1d34e59 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt @@ -1,5 +1,6 @@ package com.mineinabyss.geary.modules +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.datatypes.maps.SynchronizedArrayTypeMap import com.mineinabyss.geary.engine.archetypes.EntityByArchetypeProvider @@ -9,16 +10,20 @@ import com.mineinabyss.geary.engine.archetypes.EntityByArchetypeProvider * No pipeline tasks are run, and the engine won't be scheduled for ticking. * Engine ticks may still be called manually. */ +//TODO swap to koin class TestEngineModule( reuseIDsAfterRemoval: Boolean = true, useSynchronized: Boolean = false, -) : ArchetypeEngineModule() { - override val records = if (useSynchronized) SynchronizedArrayTypeMap() else super.records +) : GearyModule by ArchetypeEngineModule( +) { + //TODO +// records = if (useSynchronized) SynchronizedArrayTypeMap() else ArrayTypeMap() override val entityProvider = EntityByArchetypeProvider(reuseIDsAfterRemoval) companion object : GearyModuleProviderWithDefault { override fun init(module: TestEngineModule) { - ArchetypeEngineModule.init(module) + TODO() +// ArchetypeEngineModule.init(module) } override fun start(module: TestEngineModule) = Unit diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt index b17ce19dd..0d96af1e1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt @@ -2,15 +2,21 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations +import com.mineinabyss.geary.engine.id import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.helpers.toGeary -import com.mineinabyss.geary.modules.archetypes -import com.mineinabyss.geary.modules.geary -class ArchetypeEventRunner : EventRunner { - private val eventToObserversMap = EventToObserversMap() +class ArchetypeEventRunner( + val reader: ArchetypeReadOperations, + val observerComponent: ComponentId, + val compProvider: ComponentProvider, + val records: ArrayTypeMap +) : EventRunner { + private val eventToObserversMap = EventToObserversMap(records) override fun addObserver(observer: Observer) { eventToObserversMap.addObserver(observer) } @@ -18,16 +24,15 @@ class ArchetypeEventRunner : EventRunner { private inline fun matchObservers( eventType: ComponentId, involvedComponent: ComponentId, - entity: Entity, + entity: EntityId, exec: (Observer, Archetype, row: Int) -> Unit ) { - val observerComp = geary.components.observer - val records = archetypes.records val involved = involvedComponent.withoutRole(HOLDS_DATA) // Run entity observers - records.runOn(entity) { archetype, _ -> archetype.getRelationsByKind(observerComp) }.forEach { relation -> - val observerList = Relation.of(relation).target.toGeary().get() ?: return@forEach + + records.runOn(entity) { archetype, _ -> archetype.getRelationsByKind(observerComponent) }.forEach { relation -> + val observerList = reader.get(Relation.of(relation).target, compProvider.id()) as? EventToObserversMap ?: return@forEach observerList[eventType]?.forEach(involved, entity, exec) } @@ -39,7 +44,7 @@ class ArchetypeEventRunner : EventRunner { eventType: ComponentId, eventData: Any?, involvedComponent: ComponentId, - entity: Entity, + entity: EntityId, ) { matchObservers(eventType, involvedComponent, entity) { observer, archetype, row -> // Observer may change the entity record, so we must get each time. diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventRunner.kt index e620ea7f2..904d774f2 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventRunner.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId interface EventRunner { fun addObserver(observer: Observer) @@ -10,6 +11,6 @@ interface EventRunner { eventType: ComponentId, eventData: Any?, involvedComponent: ComponentId, - entity: Entity, + entity: EntityId, ) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventToObserversMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventToObserversMap.kt index 0f6d44592..3060603ec 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventToObserversMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/EventToObserversMap.kt @@ -3,13 +3,16 @@ package com.mineinabyss.geary.observers import androidx.collection.LongSparseArray import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.getOrPut +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -class EventToObserversMap { +class EventToObserversMap( + val records: ArrayTypeMap, +) { private val eventToObserverMap = LongSparseArray() fun addObserver(observer: Observer) { observer.listenToEvents.forEach { event -> - eventToObserverMap.getOrPut(event.toLong()) { ObserverList() }.add(observer) + eventToObserverMap.getOrPut(event.toLong()) { ObserverList(records) }.add(observer) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/Observer.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/Observer.kt index add0c7bc4..f615e1122 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/Observer.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/Observer.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.systems.query.Query @@ -16,5 +17,5 @@ data class Observer( ) fun interface ObserverHandle { - fun run(entity: Entity, data: Any?, involvedComponent: ComponentId?) + fun run(entity: EntityId, data: Any?, involvedComponent: ComponentId?) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ObserverList.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ObserverList.kt index f8ae57361..3a9209973 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ObserverList.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ObserverList.kt @@ -4,13 +4,15 @@ import androidx.collection.LongSparseArray import androidx.collection.MutableObjectList import androidx.collection.mutableObjectListOf import com.mineinabyss.geary.datatypes.ComponentId -import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.getOrPut +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.helpers.NO_COMPONENT -import com.mineinabyss.geary.modules.archetypes -class ObserverList { +class ObserverList( + val records: ArrayTypeMap, +) { val involved2Observer = LongSparseArray>() fun add(observer: Observer) { @@ -21,9 +23,7 @@ class ObserverList { } } - inline fun forEach(involvedComp: ComponentId, entity: GearyEntity, exec: (Observer, Archetype, row: Int) -> Unit) { - val records = archetypes.records - + inline fun forEach(involvedComp: ComponentId, entity: EntityId, exec: (Observer, Archetype, row: Int) -> Unit) { involved2Observer[0L]?.forEach { records.runOn(entity) { archetype, row -> exec(it, archetype, row) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt index a05cd7978..ff65bd83f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt @@ -3,6 +3,8 @@ package com.mineinabyss.geary.observers.builders import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.GearyEntityType import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.engine.ComponentProvider +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.systems.query.Query import com.mineinabyss.geary.systems.query.ShorthandQuery @@ -40,6 +42,7 @@ data class QueryInvolvingObserverBuilder( } data class ObserverBuilder( + val comp: ComponentProvider, val events: ObserverEventsBuilder, val involvedComponents: EntityType, val matchQueries: List = emptyList(), @@ -53,7 +56,7 @@ data class ObserverBuilder( override fun exec(handle: Context.() -> Unit): Observer { val observer = Observer( matchQueries, - family { matchQueries.forEach { add(it.buildFamily()) } }, + family(comp) { matchQueries.forEach { add(it.buildFamily()) } }, involvedComponents, GearyEntityType(events.listenToEvents), events.mustHoldData, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt index d1723bee0..d0116fd4a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt @@ -1,10 +1,10 @@ package com.mineinabyss.geary.observers.builders import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.engine.ComponentProvider +import com.mineinabyss.geary.engine.id import com.mineinabyss.geary.helpers.NO_ENTITY -import com.mineinabyss.geary.helpers.cId -import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.systems.query.Query import com.mineinabyss.geary.systems.query.QueryShorthands @@ -12,25 +12,25 @@ import com.mineinabyss.geary.systems.query.ShorthandQuery data class ObserverWithoutData( override val listenToEvents: List, - override val module: GearyModule, + override val world: Geary, override val onBuild: (Observer) -> Unit, ) : ObserverEventsBuilder() { override val mustHoldData: Boolean = false - inline fun or() = copy(listenToEvents = listenToEvents + componentId()) + inline fun or() = copy(listenToEvents = listenToEvents + comp.id()) private val context = object : ObserverContext { - override var entity: Entity = NO_ENTITY + override var entity: Entity = world.NO_ENTITY } - override fun provideContext(entity: GearyEntity, data: Any?): ObserverContext { - context.entity = entity + override fun provideContext(entity: EntityId, data: Any?): ObserverContext { + context.entity = GearyEntity(entity, world) return context } } data class ObserverWithData( override val listenToEvents: List, - override val module: GearyModule, + override val world: Geary, override val onBuild: (Observer) -> Unit, ) : ObserverEventsBuilder>() { override val mustHoldData: Boolean = true @@ -38,50 +38,60 @@ data class ObserverWithData( private val context = object : ObserverContextWithData { var data: R? = null override val event: R get() = data!! - override var entity: Entity = NO_ENTITY + override var entity: Entity = world.NO_ENTITY } - override fun provideContext(entity: GearyEntity, data: Any?): ObserverContextWithData { - context.entity = entity + override fun provideContext(entity: EntityId, data: Any?): ObserverContextWithData { + context.entity = GearyEntity(entity, world) context.data = data as R return context } } abstract class ObserverEventsBuilder : ExecutableObserver { + abstract val world: Geary abstract val listenToEvents: List - abstract val module: GearyModule abstract val mustHoldData: Boolean abstract val onBuild: (Observer) -> Unit - abstract fun provideContext(entity: GearyEntity, data: Any?): Context + val comp: ComponentProvider get() = world.module.componentProvider + + abstract fun provideContext(entity: EntityId, data: Any?): Context fun involving(components: EntityType): ObserverBuilder { - return ObserverBuilder(this, components) + return ObserverBuilder(comp, this, components) } inline fun involving(size1: QueryShorthands.Size1? = null): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf(componentId())) + return ObserverBuilder(comp, this, entityTypeOf(comp.id())) } inline fun involving(size2: QueryShorthands.Size2? = null): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf(cId(), cId())) + return ObserverBuilder(comp, this, entityTypeOf(comp.id(), comp.id())) } inline fun involving(size3: QueryShorthands.Size3? = null): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId())) + return ObserverBuilder(comp, this, entityTypeOf(comp.id(), comp.id(), comp.id())) } inline fun involving(size4: QueryShorthands.Size4? = null): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId(), cId())) + return ObserverBuilder( + comp, + this, + entityTypeOf(comp.id(), comp.id(), comp.id(), comp.id()) + ) } inline fun involving(size5: QueryShorthands.Size5? = null): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf(cId(), cId(), cId(), cId(), cId())) + return ObserverBuilder( + comp, + this, + entityTypeOf(comp.id(), comp.id(), comp.id(), comp.id(), comp.id()) + ) } fun involvingAny(): ObserverBuilder { - return ObserverBuilder(this, entityTypeOf()) + return ObserverBuilder(comp, this, entityTypeOf()) } override fun filter(vararg queries: Query) = involvingAny().filter(*queries) @@ -91,6 +101,6 @@ abstract class ObserverEventsBuilder : ExecutableObserver { fun involving(involvingQuery: Q) = QueryInvolvingObserverBuilder( involvingQuery, - ObserverBuilder(this, involvingQuery.involves, listOf(involvingQuery)) + ObserverBuilder(comp, this, involvingQuery.involves, listOf(involvingQuery)) ) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt index 6d0f139ad..e06ba94bd 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt @@ -5,22 +5,23 @@ import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.ArchetypeEngineModule import com.mineinabyss.geary.observers.EventToObserversMap import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.observers.builders.* inline fun GearyEntity.observe(): ObserverEventsBuilder { - return observe(componentId()) + return observe(world.componentId()) } inline fun GearyEntity.observeWithData(): ObserverEventsBuilder> { - return observeWithData(componentId()) + return observeWithData(world.componentId()) } fun GearyEntity.attachObserver(observer: Observer) { - val observerEntity = entity { - set(EventToObserversMap().apply { addObserver(observer) }) + val observerEntity = world.entity { + // TODO avoid cast + set(EventToObserversMap((world.module as ArchetypeEngineModule).records).apply { addObserver(observer) }) addRelation(this@attachObserver) // Remove entity when original is removed } //TODO remove when prefabs auto propagate component adds down @@ -29,10 +30,10 @@ fun GearyEntity.attachObserver(observer: Observer) { } fun GearyEntity.observe(vararg events: ComponentId): ObserverEventsBuilder { - return ObserverWithoutData(events.toList(), geary, ::attachObserver) + return ObserverWithoutData(events.toList(), world, ::attachObserver) } fun GearyEntity.observeWithData(vararg events: ComponentId): ObserverEventsBuilder> { - return ObserverWithData(events.toList(), geary, ::attachObserver) + return ObserverWithData(events.toList(), world, ::attachObserver) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/events/EntityEvents.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/events/EntityEvents.kt index 37837d8a2..ad1dfe00c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/events/EntityEvents.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/events/EntityEvents.kt @@ -1,7 +1,7 @@ package com.mineinabyss.geary.observers.events -import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId class OnEntityRemoved -class OnExtend(val baseEntity: Entity) +class OnExtend(val baseEntity: EntityId) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt index 52f858661..d358a223a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt @@ -1,32 +1,31 @@ package com.mineinabyss.geary.observers.queries import com.mineinabyss.geary.datatypes.Entity -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.builders.ObserverContext import com.mineinabyss.geary.observers.events.OnFirstSet import com.mineinabyss.geary.observers.events.OnRemove -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.ShorthandQuery -fun GearyModule.cacheGroupedBy( +fun Geary.cacheGroupedBy( query: Q, - groupBy: ObserverContext.(Q) -> T + groupBy: ObserverContext.(Q) -> T, ): QueryGroupedBy { return object : QueryGroupedBy(query, this) { override fun ObserverContext.groupBy(query: Q): T = groupBy(query) } } -fun GearyModule.cacheAssociatedBy( +fun Geary.cacheAssociatedBy( query: Q, - associateBy: ObserverContext.(Q) -> T + associateBy: ObserverContext.(Q) -> T, ): QueryAssociatedBy { return object : QueryAssociatedBy(query, this) { override fun ObserverContext.associateBy(query: Q): T = associateBy(query) } } -abstract class QueryGroupedBy(private val query: Q, geary: GearyModule) { +abstract class QueryGroupedBy(private val query: Q, geary: Geary) { private val map = mutableMapOf>() abstract fun ObserverContext.groupBy(query: Q): T @@ -51,7 +50,7 @@ abstract class QueryGroupedBy(private val query: Q, geary } } -abstract class QueryAssociatedBy(private val query: Q, geary: GearyModule) { +abstract class QueryAssociatedBy(private val query: Q, geary: Geary) { private val map = mutableMapOf() abstract fun ObserverContext.associateBy(query: Q): T 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 770fb0359..2163f00bc 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 @@ -4,14 +4,20 @@ 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.MutableFamily +import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable +import com.mineinabyss.geary.modules.ArchetypeEngineModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.QueriedEntity +import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.di.DIContext import kotlin.reflect.typeOf abstract class AccessorOperations { + abstract val world: Geary abstract val cacheAccessors: Boolean @PublishedApi @@ -23,16 +29,16 @@ abstract class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ protected inline fun QueriedEntity.get(): ComponentAccessor { return addAccessor { - ComponentAccessor(null, componentId().withRole(HOLDS_DATA)) + ComponentAccessor(world.module.componentProvider, null, world.componentId().withRole(HOLDS_DATA)) } } protected inline fun QueriedEntity.getPotentiallyNullable(): ReadOnlyAccessor { val t = typeOf() return addAccessor { - val id = componentId(t).withRole(HOLDS_DATA) - val compAccessor = ComponentAccessor(null, id) - if(t.isMarkedNullable) + val id = world.componentId(t).withRole(HOLDS_DATA) + val compAccessor = ComponentAccessor(world.module.componentProvider, null, id) + if (t.isMarkedNullable) ComponentOrDefaultAccessor(compAccessor, id) { null } else compAccessor } as ReadOnlyAccessor @@ -40,7 +46,7 @@ abstract 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 { - return addAccessor { ComponentAccessor(null, Relation.of().id) } + return addAccessor { ComponentAccessor(world.module.componentProvider, null, Relation.of(world).id) } } inline fun addAccessor(create: () -> T): T { @@ -89,22 +95,23 @@ abstract class AccessorOperations { * - Note: nullability rules are still upheld with [Any]. */ protected inline fun QueriedEntity.getRelations(): RelationsAccessor { - return addAccessor { RelationsAccessor(null, componentIdWithNullable(), componentIdWithNullable()) } + return addAccessor { RelationsAccessor(world.module.componentProvider, null, world.componentIdWithNullable(), world.componentIdWithNullable()) } } /** @see getRelations */ protected inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { return addAccessor { RelationsWithDataAccessor( + world.module.componentProvider, null, - componentIdWithNullable(), - componentIdWithNullable() + world.componentIdWithNullable(), + world.componentIdWithNullable() ) } } protected operator fun QueriedEntity.invoke(init: MutableFamily.Selector.And.() -> Unit) { - val family = com.mineinabyss.geary.datatypes.family.family(init) + val family = world.family(init) extraFamilies.add(family) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt index 1a1b52ae2..903dfe9a9 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.helpers.toGeary @@ -13,6 +14,6 @@ data class RelationWithData( val targetData: T, val relation: Relation, ) { - val kind: Entity = relation.kind.toGeary() - val target: Entity = relation.target.toGeary() + val kind: EntityId = relation.kind + val target: EntityId = relation.target } 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 58823cfa8..f6d5febe7 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,7 +5,9 @@ import androidx.collection.mutableObjectListOf import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadWriteAccessor @@ -14,10 +16,11 @@ import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) class ComponentAccessor( + comp: ComponentProvider, override val originalAccessor: Accessor?, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { - override val family = family { hasSet(id) } + override val family = family(comp) { hasSet(id) } private var cachedIndex = -1 private var cachedDataArray: MutableObjectList = mutableObjectListOf() 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 80f590b0f..f8abc4372 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 @@ -5,18 +5,21 @@ 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.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.Query class RelationsAccessor( + comp: ComponentProvider, override val originalAccessor: Accessor?, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>, FamilyMatching { - override val family = family { hasRelation(kind, target) } + override val family = family(comp) { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null 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 9508c23e9..1e98d1ea2 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 @@ -5,7 +5,9 @@ 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.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor @@ -14,11 +16,12 @@ import com.mineinabyss.geary.systems.query.Query @OptIn(UnsafeAccessors::class) class RelationsWithDataAccessor( + comp: ComponentProvider, override val originalAccessor: Accessor?, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>>, FamilyMatching { - override val family = family { hasRelation(kind, target) } + override val family = family(comp) { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null 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 index 7e40e3139..b31157e8c 100644 --- 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 @@ -6,38 +6,3 @@ import com.mineinabyss.geary.observers.builders.ObserverWithData import com.mineinabyss.geary.observers.builders.ObserverWithoutData import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.geary.systems.query.Query - - -fun GearyModule.cache( - query: T, -): CachedQuery { - return queryManager.trackQuery(query) -} - -inline fun GearyModule.observe(): ObserverWithoutData { - return ObserverWithoutData(listOf(componentId()), this) { - eventRunner.addObserver(it) - } -} - -inline fun GearyModule.observeWithData(): ObserverWithData { - return ObserverWithData(listOf(componentId()), this) { - eventRunner.addObserver(it) - } -} - -fun GearyModule.system( - query: T -): SystemBuilder { - val defaultName = Throwable().stackTraceToString() - .lineSequence() - .drop(2) // First line error, second line is this function - .first() - .trim() - .substringBeforeLast("(") - .substringAfter("$") - .substringAfter("Kt.") - .substringAfter("create") - - return SystemBuilder(defaultName, query, pipeline) -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt index 8f5c402ed..adb4f5c75 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt @@ -2,10 +2,10 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.annotations.optin.ExperimentalGearyApi import com.mineinabyss.geary.annotations.optin.UnsafeAccessors +import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.modules.archetypes class CachedQuery internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() @@ -68,7 +68,7 @@ class CachedQuery internal constructor(val query: T) { val accessors = cachingAccessors // current archetype - var archetype = archetypes.archetypeProvider.rootArchetype // avoid nullable perf loss + var archetype = query.module.write.archetypeProvider.rootArchetype // avoid nullable perf loss var upTo = 0 // current entity @@ -174,7 +174,8 @@ class CachedQuery internal constructor(val query: T) { inline fun mapWithEntity(crossinline run: T.(T) -> R): List> { val deferred = mutableListOf>() forEach { - deferred.add(Deferred(run(it), it.unsafeEntity)) + // TODO use EntityList instead + deferred.add(Deferred(run(it), Entity(it.unsafeEntity, world))) } return deferred } @@ -182,7 +183,7 @@ class CachedQuery internal constructor(val query: T) { @OptIn(UnsafeAccessors::class) fun entities(): List { val entities = mutableListOf() - forEach { entities.add(it.unsafeEntity) } + forEach { entities.add(Entity(it.unsafeEntity, world)) } return entities } } 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 487022130..e0ccb7a35 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,25 +1,33 @@ package com.mineinabyss.geary.systems.query import com.mineinabyss.geary.annotations.optin.UnsafeAccessors -import com.mineinabyss.geary.datatypes.Entity +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 com.mineinabyss.geary.modules.ArchetypeEngineModule +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching open class QueriedEntity( - override val cacheAccessors: Boolean + final override val world: Geary, + final override val cacheAccessors: Boolean, ) : AccessorOperations() { + val module = + world.module as? ArchetypeEngineModule ?: error("QueriedEntity can only be used with the ArchetypeEngineModule") + + @PublishedApi + @UnsafeAccessors + internal var archetype = (world.module as ArchetypeEngineModule).write.archetypeProvider.rootArchetype internal val extraFamilies: MutableList = mutableListOf() internal val props: MutableMap = mutableMapOf() - fun buildFamily(): Family.Selector.And = family { + fun buildFamily(): Family.Selector.And = world.family { accessors .filterIsInstance() .mapNotNull { it.family } @@ -34,10 +42,6 @@ open class QueriedEntity( cachingAccessors.forEach { it.updateCache(archetype) } } - @PublishedApi - @UnsafeAccessors - internal var archetype = archetypes.archetypeProvider.rootArchetype - @PublishedApi @UnsafeAccessors internal var row = 0 @@ -45,6 +49,6 @@ open class QueriedEntity( private var delegate: GearyEntity? = null @UnsafeAccessors - val unsafeEntity: Entity + val unsafeEntity: EntityId get() = this.archetype.getEntity(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 12631f7db..5043dfc73 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,12 @@ package com.mineinabyss.geary.systems.query +import com.mineinabyss.geary.modules.ArchetypeEngineModule +import com.mineinabyss.geary.modules.Geary 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) { +abstract class Query(world: Geary) : QueriedEntity(world, cacheAccessors = true) { /** Automatically matches families for any accessor that's supposed to match a family. */ operator fun T.provideDelegate( thisRef: Any, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt index fd4748f7e..9c8ca59de 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt @@ -5,20 +5,21 @@ import com.mineinabyss.geary.datatypes.entityTypeOf import com.mineinabyss.geary.datatypes.family.MutableFamily import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.helpers.cId +import com.mineinabyss.geary.modules.Geary import kotlin.jvm.JvmName -abstract class ShorthandQuery : Query() { +abstract class ShorthandQuery(world: Geary) : Query(world) { abstract val involves: EntityType } -abstract class ShorthandQuery1 : ShorthandQuery() { +abstract class ShorthandQuery1(world: Geary) : ShorthandQuery(world) { val comp1 get() = component1() abstract operator fun component1(): A } -abstract class ShorthandQuery2 : ShorthandQuery() { +abstract class ShorthandQuery2(world: Geary) : ShorthandQuery(world) { val comp1 get() = component1() val comp2 get() = component2() @@ -26,7 +27,7 @@ abstract class ShorthandQuery2 : ShorthandQuery() { abstract operator fun component2(): B } -abstract class ShorthandQuery3 : ShorthandQuery() { +abstract class ShorthandQuery3(world: Geary) : ShorthandQuery(world) { val comp1 get() = component1() val comp2 get() = component2() val comp3 get() = component3() @@ -36,7 +37,7 @@ abstract class ShorthandQuery3 : ShorthandQuery() { abstract operator fun component3(): C } -abstract class ShorthandQuery4 : ShorthandQuery() { +abstract class ShorthandQuery4(world: Geary) : ShorthandQuery(world) { val comp1 get() = component1() val comp2 get() = component2() val comp3 get() = component3() @@ -48,7 +49,7 @@ abstract class ShorthandQuery4 : ShorthandQuery() { abstract operator fun component4(): D } -abstract class ShorthandQuery5 : ShorthandQuery() { +abstract class ShorthandQuery5(world: Geary) : ShorthandQuery(world) { val comp1 get() = component1() val comp2 get() = component2() val comp3 get() = component3() @@ -63,16 +64,16 @@ abstract class ShorthandQuery5 : ShorthandQuery() { } -fun query() = object : Query() {} +fun Geary.query() = object : Query(this) {} -fun query(match: MutableFamily.Selector.And.() -> Unit) = object : Query() { - override fun ensure() = this { add(family(match)) } +fun Geary.query(match: MutableFamily.Selector.And.() -> Unit) = object : Query(this) { + override fun ensure() = this { add(world.family(match)) } } -inline fun query( +inline fun Geary.query( size1: QueryShorthands.Size1? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null -) = object : ShorthandQuery1() { +) = object : ShorthandQuery1(this) { override val involves = entityTypeOf(cId()) override fun ensure() { filterFamily?.let { this { it() } } @@ -83,10 +84,10 @@ inline fun query( override fun component1() = accessor1.get(this) } -inline fun query( +inline fun Geary.query( size2: QueryShorthands.Size2? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, -) = object : ShorthandQuery2() { +) = object : ShorthandQuery2(this) { override val involves = entityTypeOf(cId(), cId()) override fun ensure() { filterFamily?.let { this { it() } } @@ -100,10 +101,10 @@ inline fun query( } -inline fun query( +inline fun Geary.query( size3: QueryShorthands.Size3? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, -) = object : ShorthandQuery3() { +) = object : ShorthandQuery3(this) { override val involves = entityTypeOf(cId(), cId(), cId()) override fun ensure() { filterFamily?.let { this { it() } } @@ -118,10 +119,10 @@ inline fun query( override fun component3(): C = accessor3.get(this) } -inline fun query( +inline fun Geary.query( size4: QueryShorthands.Size4? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, -) = object : ShorthandQuery4() { +) = object : ShorthandQuery4(this) { override val involves = entityTypeOf(cId(), cId(), cId(), cId()) override fun ensure() { filterFamily?.let { this { it() } } @@ -138,10 +139,10 @@ inline fun query( override fun component4(): D = accessor4.get(this) } -inline fun query( +inline fun Geary.query( size5: QueryShorthands.Size5? = null, noinline filterFamily: (MutableFamily.Selector.And.() -> Unit)? = null, -) = object : ShorthandQuery5() { +) = object : ShorthandQuery5(this) { override val involves = entityTypeOf(cId(), cId(), cId(), cId()) override fun ensure() { filterFamily?.let { this { it() } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt index 329408654..04161d465 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt @@ -2,32 +2,45 @@ package com.mineinabyss.geary.datatypes import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap +import com.mineinabyss.geary.engine.ComponentProvider +import com.mineinabyss.geary.engine.Components import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactly import org.junit.jupiter.api.Test +import kotlin.reflect.KClassifier + +class MockComponentProvider : ComponentProvider{ + override val types: Components = Components(this) + + override fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId { + return 0uL //TODO increment + } +} class Family2ObjectArrayMapTest { + val comp = MockComponentProvider() + @Test fun addAndRemoveOne() { - val familyMap = Family2ObjectArrayMap() + val familyMap = Family2ObjectArrayMap(comp.types) familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) - familyMap.match(family { has(1uL) }) shouldContainExactly listOf("a") + familyMap.match(family(comp) { has(1uL) }) shouldContainExactly listOf("a") familyMap.remove("a") - familyMap.match(family { has(1uL) }).shouldBeEmpty() + familyMap.match(family(comp) { has(1uL) }).shouldBeEmpty() } @Test fun addAndRemoveReplacingWithOther() { - val familyMap = Family2ObjectArrayMap() + val familyMap = Family2ObjectArrayMap(comp.types) familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) familyMap.add("b", EntityType(ulongArrayOf(1uL, 3uL))) - familyMap.match(family { has(1uL) }).shouldContainExactly("a", "b") + familyMap.match(family(comp) { has(1uL) }).shouldContainExactly("a", "b") familyMap.remove("a") - familyMap.match(family { + familyMap.match(family(comp) { has(1uL) }).shouldContainExactly("b") - familyMap.match(family { + familyMap.match(family(comp) { has(1uL) has(2uL) }).shouldContainExactly() @@ -35,13 +48,13 @@ class Family2ObjectArrayMapTest { @Test fun removeLastInArray() { - val familyMap = Family2ObjectArrayMap() + val familyMap = Family2ObjectArrayMap(comp.types) familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) familyMap.add("b", EntityType(ulongArrayOf(1uL, 3uL))) - familyMap.match(family { has(1uL) }).shouldContainExactly("a", "b") + familyMap.match(family(comp) { has(1uL) }).shouldContainExactly("a", "b") familyMap.remove("a") familyMap.remove("b") - familyMap.match(family { + familyMap.match(family(comp) { has(1uL) }).shouldBeEmpty() } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt index e9040d324..20ec236ba 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt @@ -10,10 +10,11 @@ import org.junit.jupiter.api.Test internal class FamilyTest: GearyTest() { private sealed class RelatesTo + val comp = MockComponentProvider() @Test fun contains() { - val family = family { + val family = family(comp) { has(1uL, 2uL, 3uL) } (EntityType(listOf(1uL, 2uL)) in family) shouldBe false diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt index 8cc63eb7f..4fd7f4137 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt @@ -1,5 +1,6 @@ package com.mineinabyss.geary.helpers.tests +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary import com.mineinabyss.idofront.di.DI @@ -10,17 +11,21 @@ import kotlinx.coroutines.withContext import org.junit.jupiter.api.AfterAll abstract class GearyTest { + private var _geary: Geary? = null + val geary get() = _geary!! + init { startEngine() } fun startEngine() { - geary(TestEngineModule) + _geary = geary(TestEngineModule).start() } @AfterAll fun clearEngine() { DI.clear() + _geary = null } /** Recreates the engine. */ 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 8ba6c4991..28c01058a 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,15 +1,10 @@ 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 -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/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 12612 zcmY+pRa6|n(lttO3GVLh?(Xh3xVuAe26uONcL=V5;I6?T_zdn2`Oi5I_gl9gx~lft zRjVKRp?B~8Wyrx5$mS3|py!Njy{0Wt4i%@s8v88pK z6fPNA45)|*9+*w5kcg$o)}2g}%JfXe6l9ig4T8ia3Hlw#3f^fAKW63%<~GZJd-0YA z9YjleCs~#Y?V+`#nr+49hhsr$K$k!lg}AZDw@>2j=f7t~5IW6#K|lAX7|^N}lJ)I!km`nrwx> z))1Es16__aXGVzQM0EC8xH+O!nqTFBg9Ci{NwRK*CP<6s`Gq(~#lqb(zOlh6ZDBK* zr$|NDj^s6VanrKa+QC;5>twePaexqRI%RO~OY075y?NN90I|f^(P# zF=b>fZ73b5JzD`#GC3lTQ_B3lMeBWgQUGYnFw*HQC}^z{$6G4j(n4y-pRxPT(d2Wgb%vCH(?+t&Pj z)QM`zc`U`+<~D+9E{4Uj2kc#*6eZMU$4Oj6QMfA^K!rbl`iBix=2sPrs7j@aqIrE zTaZJ2M09>rp$mgyUZ!r2$UK{+DGqgl`n;*qFF~M(r#eh`T{MO?2&j?xgr8FU$u3-` zhRDc_I23LL4)K&xg$^&l-W=!Jp-P(_Ie07q>Je;QLxi8LaEc%;WIacJD_T69egF?7 z;I_Sg_!+qrur8$Hq4grigaiVF>U7uWJ@Hkd&%kmFnQN-P^fq0gB1|uRt!U#X;DnlV zo?yHWTw7g5B;#xxY`adhi4yZn@f(7-Xa(J6S=#d@&rlFw!qfvholE>MEb|VWn^g}G zMSrK&zQ^vDId&ojL!{%{o7?s{7;{+u%L{|tar(gp?Uxq3p?xAysB>0E$eG#$tvkk9 z2Q2gEP17{U6@UD*v({5MP-CTZfvWMItVjb4c;i~WLq&{?Q1(koX&vt7+$z}10{^Id z{KDjGi0JpD7@;~odF__0m|p;5rIrHidOP9^mwKe#-&JX-X@acc)06G{LO1Wu)#gvZ za~y9(fhA%UwkDOVU1LBJ`0ROE z4&)dJKK%mG@+CIm?+wt9f~@xIMr8}UH*K1j| z0pppo{7gv3v{URwxVMeg>Ps!L5IKxm zjac2egjgb0vH5i75$s|sY_RYec#>faqJk|AGgV;v=^%BM(^p{p;(^SVt-88G9f!q; z>p}9E4^f0=01S2pQBE4}9YqE%TV)*hlU^8k9{&=K76+*Ax^r=AkBb%OCP^P2nm0Ri z;D-|Zk?gGeU<12ti2CnPVNA(Pb)02+r|&yTWW-OJO7 zNLb0pps6aN?A~NJp5kj{{IOlf!5KWMleV@-hYLift)D>-7K+tgs=7Ake}oBnIy-y1 z(Hn@Hjw=_(x>dO5ysQsrnE%A*bk0K<-j{1Yqz@#n#jOL^AzCr#wR|WYzqk6i7v)Lf zkXdKxzuu20aP{Tbg$(+9&oh7cd(Uoqqf<#ujb$q4sZ~gxFbQfS zS)kNklyL*{2AELgjZ(LBu*>S(oH5AaJ;YiB@;l@=O%F6B?oanzoYRM^fQ9-<~^=3$H0g^JPMLQo@SZ@QuNvy)tyJ)LSj`+()#fy?{aV4Yg^7dlQ7AQM^3GLCR2dAFR zJjtfKiVqF`l-H_fz0HD|9g>)pOxn}k!vdZ=DO!7Sikm{Z%P6BrRkBS6W?ZB5W&7rT z@uYpf@M@a!z7H&o@-yrcCL^Ff3e7p3T`R9p?@o-acXmbTSa0>ZANzCSgovsd%;i$| zVus`not!oL#(W`L-!9w0jdaECaG4hk{V7IOs676ZquZH~0TX5hDq|)x z6T497l|E?f4)LA>j=S8}b$0LS=I4h|hUFJYJODT8Li@#6kF$k0)@*l{RnM1HQ%?VT ze-Pqlc!~t(oumVC*?5fwR;P6u{tHaZ~*LlD;B)4f? z?lpWfa2P@)g57flVl83Ej%P`2)gGyaPjhvD(%i~{`2b>#3!+y&` z!2nuwHMFA-zUY}f1^0B8<`N)Gr=A4TS@b1qykmd0Pq{?r)+1^^+D(=xasb^Tf!oK9 zBLL+*p6M_#ufgLzgq1zcSwZsZnQWFLC3`Yxdg-2=*tT`J9nrfYt)RF)YryBf8_gW{ zvKbB+oZLehfT)S#<|y1)E0hW^?+AnqPXq9Hu;v3dsMGdr{SVyF63;K<8VcgI#~}1i zLYSBL0K;RTT(;>2x=*!1Di9w0mwr;`CN}kM65|Ay{~z}_^JKOsRaN<~#9O^iiW<5P zYN7r~HV!#Nz~IZU`P>1Xe%4f~K}KcF#X&5kO*G}-)74S*tQ8CietdPcA1Yl;S=Mr# z`#MYY!{s^uo=jn7;k6O%(}fN+*0cWMpt~#n9DR<3NyU?+3D^AgI}S)Cu-Tljg`VY} zX1=fq$?8$DtOeGxE6f8lbS_6Q3C4+LDTO$}_IpM$Xv<|QSC%+Oll^q$y`7o@jD{dp zNDl|&X)r7wETa-#h*d`KXntxI(Y{vLha{$0i7@G8xx^m=c<{lJ9?p-i!^W{%j7-oo z0W^SzZ^(Wkyz*We{lEn%Yhu-ycUOHtrRiVJL4~&S91*D0MrLu}Q>v-Mc?GcWfpyz% zX|UvcN@krFO#@v|CtYM}g|=L3%aMo$E5<@CM%c*;?u>LOTz00@+dt1{yg1y=$h+{|D17U}$*^fE^H&8b431EUE z<9tv0V_#%#&1N#j7AKCj!tTK@J%oFW*ESW<(#Gl#Xs%v<@AitI?s92nLzm<)w3Wkkom1f$gcdUi%g_*jofy&}N#luL<$GVIe{iQkQ)sIHVy zBgItnPBFamrv6Kb{eE($Q(f`ZPeW!Hm%Y@F*OF1sKB{Yy|C>WEv_mfvv-N-jh)B-5 z4a!1WcT@9a+hGaBrc~sz=>G?Q!*Zp^JFRUvBMyNR1;`)j$RhH$6gEyVKhd$&K-CFT zXaWC-Y=fyOnqT84iMn9o5oLEOI(_3fk!W^8-74|q1QhQ|CmT0i=b;6Z3u?E{p7V{? z;f#Q-33!L+4&QQcZ~GAqu$NS{M;u%`+#9=7^Oa5PKvCCCWNG_~l(CidS!+xr-*gg{ z$UQ`_1tLT_9jB=Hckkwu>G{s0b0F4bnR7GibmHo?>TR&<3?D;5Fb#gd8*wYa$$~ar z7epl1qM)L{kwiNjQk}?)CFpNTd?0wAOUZ|gC{Ub|c-7h~+Rm(JbdoRe!RNVBQi!M8 z+~U6E2X&KSA*T6KJvsqwqZl#1&==Dm(#b^&VAKQ>7ygv*Fyr;)q9*^F@dCTg2g!w~ z%hg)UXAUyIpIbLXJv1nZX+a_C)BOH2hUim|>=JHCRf(!dtTidb&*~I!JrfRe+PO>w z@ox$G2a3i9d_N9J=|2$y2m-P&#PTNwe!oLBZFs;z|F5kXvBDn<)WwE0E3$ow=zg3R zK(9;sf0t;VEV3@gAg7jRtnj%-6O@!Hvg*;XcUAw}!=2*aErvB(eQIm(-UGmq^J=XN zTqJo$Y|WKo^HlBF3BXJrA#}7ZLg=r*w`I*~Ix`o&2k8^(0mt8Rp=A>F`&gehhp@Jy z^e^#B2!~$LvNCKugg)8)-G%&THdk~kfextilegP9?#C#()F59U$&eo(h|5>ceo*Em z{PEE79T$YP|Kr7K`WBHbtQwyxFkCl6xX&+oUf90B5xoi3_5KHHCyEE*oPbOQkfMz& z6^hT8_NXd2iWk{q9IKae1{_7hMPH8I7_BMtVOM4 z6jm?E0QJOn$qrgsJ`9w##GB9?G})-GXSQo6(tYS(Q0-Ct$co?Zzl0?NHsDRron?;_ zZZgQg)%XW>P?8_&zoGuF(>Och2kEJXsu1_X&~w87x!b z>~h!a>e7{`p@+#hXF88wI*JeWRZ;J4ev4<}HWf|Z;(7$E!S5l9wzBHFe>^I{2`a;a)QnAwa2xv1e(bq$<}!8o^ofGvYpk7dBR+`*%iE;hUY5 zaHF}OjGO9r*{%lmcK^uFiTHgoUD`^9Nx@~;Bg!V* zuuJ&ti{DQiq7RyJAR94wem{}cPK1J(Yxnn_{=>?USqz-~&QXRStS^s-7TksZ$AEI! z#og36s3JGtGU{CnDHRFtipFqvrE*gw7_K@NN0h+ItTq@4fqN!HeQU1y7*X?9+IfZT4Vxebpt z%#VzgdDK~-&+=Z*#>=n#XUhNvBZp3=Cr41jMqwJkHLf3L7Vm~V#GgJ(Jpii~PmJ#s zA7Ft!{xD@z>9DUb4JbiUBdNEcU4BO$651iN*mp*f)HbRRM`Cx5cR?5IfEcU{IZWwf zz(M6CDv)>xa3x}K6%tP^i15P1&&DOLK=k~+jNR$UK3frSl+|PjSC-dBItvD~LL! z>_g(YYdO4k(5EbPOw+v+;G7~jYm>F@Ai|o`gs%F)F8tDz$dl7Q%aCe|v|$UkAul_R zNlA-beBX^IJU?kgS`E$it7nF4DaI!SJAGq)2P&Few(-|tp z?K+%D3e4{pfkayrcbm0ftu6Ol2ZzdKM+4i!hNP3NRL`EvvZJ3yvNr2MV%igZ4kj``Qrdb_OI$7jWP z;l0DYf&0(-*QcP5zrP`HVznW+SbH63Qx$7_9~NjRNg7eKqI!UJ=XH`g^=t8GiFTu( z?2L{JKEu%jJx&XjNzU(*!ZNmL1@RlJA0G$2_LrAb_7lmjil(GSlSM zwTes`m+3R;3#N~Xg#9owh3ycXV8@ZlaY_16kpPFA={721b~URO4HD3sp%fmkZM}k) zZB0#)kP=RkNB~R-MCk8aljG_bagt4vIb~8)BV%(b8_;)&Kf9GX+%O_cNG|(D$!3&D zL(I8}*LqN5NntipFlN13=`D>6!{D@CFMBH0kW3=HccJV+xW~|$qeFR5i-2{X+iWMu zI2$gepQ)H_B%ip_BlWOQ*|pErXs|4ir{IHccgaIJ84irE{?+$KDABXr&f`jB^V-c% z$$u`uU1YB^{<+UN2cNg#7&0bz@yF?5>j|;)5&IV3wIQp58X#OE-M^$HdyvL|Um5t? zhZlAG!Mz%XkUe3t471JM*Yur}o30vzu6RN7gJyNcf!IItsDO730mcJ*O!~V``y5=3 zNJGp34DZ}wd1H6V`Uuy%es>BiO_aE-S8jzir#$& zyk)@2a5tP$@g%jW^b^JGdo)X@Q%sE`^lDQmY9m%uDFpPX`w9%=yQ+nneMm#OaXcD` z9}{tn5A2b2z9783vL2_jSao?uxJhWJoq%47*RafM4o0@gY(p)F>qT4^XM5GLzV#6j zC+HoGhAne7o_w{WUo(B++z7lU3Y0k1rYv9|TSv0vR-Du(5=VakbbelgZTeDn+a_Wv zq_j-^+Qz1WAl;Zg>ahX|CERbX1V%B!hTKN?M}fGoA07M(WU&NfT&TmN`P@56U2 z^)vLDs|Ln~0iTtn-?KTeQl@T&bskJFuTUS!m+$CS9vnd}8(UMO|Kv6TCfGN9NUu&4 zL{)GTxPq>fwsJ~aU=4Qhuq8*RzDsP(LZh$BHezq&9gK$IS<|DYbm})$QTGCS6T;Dr zEkLct!b+#<1r9OKG@P!f1wm8>=Nz!7OzJm!g<+`?N3;YaA3(P@EL=(sTaRMDD!c8=-XN^4BXp(eVkj$NmEMYPP>YJ4bJ3yUud z<3BeJAJ$6z^TuywnfH5lv#$lgwraNw{IV=tIznPH1DT`v-5yS=!)J<}xxl}uZf9azA2A97Haf!;<3y01hlw?dWNEv@TLi1s-mO4vmIT%O_42nS z$VRWrs9NngqRRkWAnWkn%`Rw@?wH|)7XL`EL5EZu$qyJW31&CB^T_)qwIv!{;E_6 zo-9XAryQRlk-O0>o#-SZO>|6OYq;}<*>Wu1AsVRiXY4f8qb;+sItv3AyS!4Ry+q}) zA!pAB|BmC;=RIOk^^vlsEH(!Q!7_1FK~ZB2err*o!+b(r=m1b?$6d!%zmN+69LXnT z&gRmM+n_R-F@sT*IYv0_mGPvur!u`iWbQO7SqiGFLeY&yga zf`lM&B74FA2C?N@8_z652fjhBEoDUKbP8hL{0{HAF%qDo7)o3=3rg#6)T7%%5^wl% z9R0*S*<~>nzYOdQk2l`9h#t+gJy_xujw6xjV(8S<_DbVg61&pT%Hi42l%D73G?adn znB%UdNM0p}lEF-P2%TAMam2zpQev71e>a$$%i+r~b+D9G9pF|oY_*(-u*89oKsXLY+UIbqq)MQ%(GYS{(*n_S_*RN$*~`zUtab%0aKwhx znc)Yo?{xq1sJCgQD)TeTci1ucvbez9q=A72H(-SB18Kl&6^vHV8^i!p@>iF!DIw17 z+8Q)TNisB7>pwyww4y)yJx*wX6SJO78eLBC-ar1+k$Z9fy;wBD|3kzI{<+l*>PSY^ z_?nLOZaeWbU@C3hfK?X;Di*8CHCPkx2qco6(ZyJdqSzp^TJ_5Lpa0UP{Gy+!b0Lr% z@xYxSjUKoY6L#>$qx~KD$-0=|OF7zhVP~ntMgEALYPIfhj@+ z!;JJ7te>CcovruwHsJH6Lta$nm|%^C@=V-rmhU{+I~0(|XHQ9jt@L7pb{gx#{4r!) zg($FyFTslcgu(~6lYr$nW?)%*l#VJ=R-jxK(x=t1bWlu(nL66T#qj%3aZ@uVhy}Co zDU_q61DD5FqqJ*#c|(M5tV)XBN?Ac^12*q)VN4yKPJ|#==S_`_QD9|0ls!`2)SwuHDRA_OfXQDq3%qW&MZB}Z!=k-9xqev8jHz(H z{^D@cIB~QiK>~wa)A&^Ll^Wi6QgCzU;iv-BHsLBs zH7=jN%|>0S`SjP%M&AF1PNVDp_FZ?2Bm@7`DC&v(pYrw!!yD#4 z6+<=HS0Ln6MhoKxF<%~H`y20{vf#pxh=;j{zY381gvAFekgG|>G1zo8$&az{V=;JR zy_puF4$L$?EMhT?;TpQoR*j16ll`#AS4e96C}yp_aGKkBe?1H|k_;gG-~Xorc<;lI zkB}fB{$c-D2mGA&{rm<*@F5)c3X+6??g~XoEwuzSuch0D@W~P5(2I8v8F$c2$Vw51 zP#YLSBDqtWW^EYBl^QYHF+MA7am6f4DOhwnJM=W9$uvMOsZ%_~?)2C#wb?CkI$7{K zEi)=#|5pFvg^){zK5kpBLjB2kZ+$ZB|L=W|aNwyyb(gC2l7bcpx{E-H@)q6@D6N^xh`{1E%ItF2$eeB_SjI@b2WgTpS1thwg&n`jiIzw^TtXUyB{00($GIq>vbj|}bav}}Q_~wp3>k8!E@hVC;OMUTu|= zAy#vXH*GrUHu7^cNZWe1>y;2(51js9wbu+R3Aa*(wzH9+X0dIsf&gc_x|_LP z>~CF^?(~U}+l~ehe|i>?4eo!xkq&Lk+RR-1duNP#o~>@1x)s&i&u zRaYL@+D&_M|JLI6fHbEr_`U;HgPTh#E3?sB)A$*gqyBgg*ql|a-m*TX5rACbWKCE6 zdeQ`v8m6>g^ugv`p|HY^#1QZrGGUj0^HVDc@{?Q0yhalbBEV{+|HzC^-{&e{5K%z9 z6Bxtnfu1!@Mp+Q&*&~;FOg&*Vm<@4b;{FG0-!UUXX!|)1w}op!B_|7_s~d(+=9Gba zKp8`LaB4D(H=cGcspJ_TjYaOwMb=sGn^gtUVhK!UI~2KKYEE-NC}F>+BEY7IVvy%KRvm00tg!Q`y=er}wpEetX}K@;}(}{s9AzV#q2@ zBy7}->|N?13POrs`;U?(qAG(I$~Gt+Rgw%aNZ_0fs_utVvRJT-7z4!@x36v@=NBX=IqkK{#Kg0w48de@?#Yb4M(Svj5=T+<ONr8-oh7l?Cji@+erqur zFhZ=9|Lk=$`c}v4u`)-!!UI=!9Jo@h&7p4RlS#u! zZ7-prn75JkV?VjptX;@$#`U`{vB!=Z?V`T*FBF>J?vsML7e6@2GbUteMFfX-TUu{2 zLNIG*;dV)8GV8gAgEf#)X3A>p3^CRka1v?~8x^anBhQ=L=LsOl=&pcOYHo98m##ye z34MtGCDK!`ptl?taGMr5q{!zVc? zG00e){TV?`YA9eB;(lA3lXI?RrB4BYQGk?vOmTIUJED=(`_*gtn2DB-t4WW54as*W zb2kD-lWX>lb$+W!VFakki>B^Vc+u$?NLF>)!U%b@Y}gYJ>m2H=^x0=nsE0TF^Yu0h ztgH8-o1%+jCk(+&`|)tTfEVHq0cMeFa{Uz)X$;fCq%Y=SOWML6bYfeP8j5hktL`KK z(18`XrUn&WN9PtFxh&dX`y~YBsmdhi7Kw%tKzM%^VEhdD<_XkulW-x=JN6OPbFI4@ zzDDRN+f=@{0h*MswwOqG6gJ?{NuHx(y-|FUGsxyZ*x0~$MW(eY>vqq4Fh#t7uzw=- zKB?|!0N~!h^AMdLa)oR!Ca#HZ9&Zf)ghuO<^RN)4twRlygHnQG(BE{cDc5E}OF4;xss6gYyV~EcJvJkX)xNWb=@yw!uq0v-sf^rvkp-;?DPWK@*SEw|V;IH=7 zfQqEV_>DjOPT~8X*J|H8=&RnzK4~S7ML~nLX^%s-Vqc^aWy7N$y57qciZGcqy#=zU zs8hcHiI=D$+RB{|62{ohCTiaML6FI4Uhzo5D{Jik@poCs0w7F)*w}F4r0sJ~#u-72 z5bK=ANt=M$Dh5NKnxGsg9NRR?WD-x|FhTwBjd zD<-K>44DB~i%frJOfnzh1R>PRY34kw!6~p3M$JLaD1r@`=h)~Ngks-(gdXh^Q?BTP zZ^Zj5w1AwtuR2$~E7s9iZdF}z%pv1em^V2rM{1tLUY@-+Sc0(9jA|iZWml1;v13=U zHf?y@#mb--7z6$ue>`qjhE~brk$AY-RG90~5wcBbDReXR2)pKg{L>;H(DI`U!MLNQ zY9rFJP@ZQ}jlcMh%WSCo%vf+nd0Gmd*F%KMIe>slCUh)8Ma|;M_I+v#;|ueg9oLg; zq2HtZX%&#F7vdpNlkX?}(C7dGC^y#NB#m4%69RzTNrk%4ol~hSI%>2r6B|*ZkW(*P z;u#s;+faHo{tfy+1L^RzWDi*^JR0iY(zJDB36y_QJ+|E-2x+cY z!V8uLNktH~q>WQZuY!Ap66WP|E!0PA1jK~)^8oJVGbspJs6QL!!-5Qm7 zHYI|_`Actg?vDzdg5{86w@GS$G6ANzff7->6i5pB$T4O}`fZ_;{217Om0gN5zTr12 z5mW{hCzCE-QubjxN$TAE-XgI-8dTY@OZmq`y+y_>dk*(qXF0{nam|q@~i}Utp*k{yurq(DW54hkDT4bbg z=_etM?Nf5W^o-HEu9_?&xEqPg^P^mTxLH8n%u$!mWvFG|{&)jtnU&6|5-`~eaNz0%D1BDo`{ zS1N5(KW5v^2eLdd_%`uaRndF@h0Uo6=M|8?b~KbOLZk{HXEnGmtgZXf2inI*1r%n! zQ3&%RI4r{f&dwW~HwH0Ked9b!k6{>_19H z_Ai>5IChDMY(FfMyG%;30?SQ{iV9KyGru62+Y)~qSQ91}b~}w<&*}R&1c#$O`H@~c z5)2S_eXx}M#N{MuGeQS9@#UJB@;W_j50b}jIhxMPloEFQZdvwxiU^RYycTzgK)-vl3LT&$L8~@68$C8~5_U{cR$E#w*x65(qw&eoL@>%ZHvj zWnEMlSh*(o&oy|J7eJ5OD`ssy%F?*Vp?`Cq;FShyl{ZoKCG5g{y}>usznni#8ki(i zO{w@n{iAj1_ooX@+s*!uW60WcH~*bNOT6z%0jVML5};wVrQp~`Uss_{cO2oud_nNA8^B$?07fJ6?iI)Q zuo9G)O-z)DqstrBqf>B%S05hf-wep0@$BFHKSrkZ{za3D)yVzRz)2{wf8(Wp+xyAM z$rtyx$gi3A=V~V!`Q3;BM0$>*VVtxEM|xDL^gew7ydy3Q6YzD&THRz*q33Ms_D;M- zbCx1Ft#UNB)V3bf`~{ImI72OTp^|bF8?G8#FRj+Biy8ET5#rA3sd|0FR@U(LAJ%w8 zS1%n8Z=Amhw)92rIsof=YVWF4jw&F*j1LG@-`+cR0-~2LqXRH8(Ccne{y#MCPncF64U`0uO zWmi$dlii~1D0rLR{qc|_2M!C$t8^=G7xQY)9!#Y331A|>N)EhmyVdLWL9I3YLJ`7? zZmpqUJB>Ni9oiL)^1IK1UoMyhWE{$9M2M6Xi zPKk7GpMsA6vjZbU7~i+u|J6Nk|Ci!Y3UMUT2|`M;JsNQACdJ%ooo9Yt{?A+0hMpxi znEa~~sxC>rKrU6bd=WRb;%wsH>A#j4{({&1GYSNR57Gama(3)2A;SM>qop}l>Jk2* zn1+C$fIxuwzg3mCU#SOqb-wOCb6mBcYlA5+mt<&_J~sBxc(GQtBFINUO~Mr7<-uu($>P HJ4oML2Lo<@i8BwbL^1~GkG`E7C$SEa_ zF^}Ea+#Je`Xy6;#D0FPnSrR%Y!QGA~NA^{oWmW8C<3dr{x6wWQ{4+bzemqV5W$i5~ z=J0jXZ>uZb>DT@0Ks?4QJ{`z?8JWl3$y;2pj#$XP*pv$>$g(z43{YH9KmmR6<#sIn zA`#=0#sgycaBQ^&}Xba!|KaZ8~b30v~nLt z9%#gz_*=~KD{3t^X~l>480*}PhKN=??g`RV|4Ud{Gyyl187MJ}r(#e+H$GEdI+p1s zq_25h;fV)$EPK%Dw-(G=f`yHB-_tttsC!?k7*#!|4a>`Ahj8nm?&n>NRs%jkZW^3-0P_yMP5&*6a26{MRj1&TPF zyE#|c)5uUHzMWx=rMKpuPih*V=S;W3MzIZTw2uTbr}8`p2bm+Z6Sa%vvWAWSf4H)p(+ zSQ8;EvUa#wqWV+9vmIio(%7wukK2SwjUS8Yl%Rq%=~PU)2$Tvm6`1!r3H@U#_|bB0 zmlT1PS3wPB(b&^+@YY7Y$n4l3mV3-X0$>z|gZp6O*Lhzn&?Gad2ZCF;+#95-Y?#y+ z?*l@Yf=a4w{Px=o!N|3~_XKfk&G;fN>Ps&dp2FpA~qD=0~=!NOS@B#XAKKkND>Y{4>rqxrViKD7;?>j8`R` z&G)3FN|dfsxnaI^!d1G%=>AbTTxZWo;n-DLrQ!sj=f~VAOe5zhGS(dgx|!ls62fbX zV@<7Ck^!}R=`Swr?(7w1rY6Nmq~sfXJ?TiKJLn=&SQdEt9$@0 zA+h1Wbwbri0s-stc8yVq;mRa6@kEf8^KXUz&jcic!+avDvvJFa>k0ioWug=T3oPw; zyj4it&0@>_*uI@2=^+T7sL1_!^aJW@Xfo8aC#3^WtQC7fET8b9C} z*u^ue6Ojn z7@(eskJ2+cNnH9~VyfIh<-|7!je~vGy*odz(sk-u$~SrYF3glruZ*W`{sqnS+9=;Z zh{D@MSG91%lr&ua8%$sJF%y1I<|e;EdfJykY8#D$Hc_81n5`$7;1N|b0tvvPLzSg& zn7!5x?T*@rQUKcUhTIjV(rw*5oQYlm5DbEO?60#mohHfbR$3_x#+PZoYi@Vd4`#YgKyTd^!4n{fN~WZDY61sAOm6 zl!d^i*a01QxpWM9Pcl?&{RgO}uq%ErOk5WpECvnfEh!*YP&1Sl)uTN4hg??Vqs~i5 zYsfufz3?{TtwuBN=`0~Qg1PlWH#OGG$ zLLWU17$v``)CE1cds_7kj8mJ{-+l8{DS|zAQ&3|qpOY=!J|kXUhXue9|H>4gqk|n) z-i34GmxLFj8asb3D#D&=ya*a5`C<=o?G;Ev^LV%;l#nH#O=7Nh@z1Do>j6Q;I5S2P zhg|AZbC&|c7}uSJt57s2IK#rSWuararn-02dkptTjo*R{c5o(bWV}_k3BBnKcE|6l zrHl&ezUyw^DmaMdDFVn<8ZY=7_{u{uW&*F<7Al6};lD(u;SB=RpIwI)PTyL=e25h* zGi{lRT}snjbMK~IUx|EGonH+w;iC2Ws)x>=5_{5$m?K z5(*1jMn%u0V1Y%m@`YS3kskt~`1p(rA4uk;Cs!w^KL$w>MH)+cP6|XKr4FfHIATJH z!EGAK4N>1yFR`-zW|w%ByRe#=&kA&#WyUldDGpt!wf-8SFWiSi!5QZL+l7*CE?u!NW1T$<1rdLJ9y3u{_zvHaM?#Rm4 zFk}^1!ffcrB|XK3gsO-s=wr*sUe&^$yN|KxrA)uW00Gu60%pw_+DcUjW`oW<35OC8 zq2{j8SgC}W$?10pvFU83(SL$%C?Kctu3*cs0aa%q!fjn1%xD*Jrm!F3HGR9-C{b?- zHp(cL;ezXMpL@0-1v0DMWddSDNZ5h?q50cOZyVi#bU3&PWE=(hpVn|M4_KYG5h9LffKNRsfhr^=SYiKg?#r&HNMi2@cd4aYL9lw(5_IvQJ zcB*DD()hUSAD^PdA0y|QrVnqwgI@pUXZXjHq3lG2OU&7sPOxxU$Y3&ytj6Qb=2#cC z;{d-{k|xI*bu+Vy&N+}{i(+1me!M;nshY_*&ZQLTGG*xNw#{RpI`3^eGfHck+*38NRgiGahkFethtVY=czJs#)VVc{T65rhU#3Vf?X)8f0)X{w!J3J{z|Sq|%?)nA+zo?$>L9@o`Kc|*7sJo4UjIqu0Ir~S5k^vEH};6K?-dZ0h*m%-1L zf!VC%YbM1~sZOG5zu&Sh>R;(md*_)kGHP)<;OA44W?y53PI%{&@MEN}9TOiqu+1a3AGetBr$c)Ao3OX>iGxmA;^^_alwS818r4Pn&uYe^;z6dh z)68T|AN=hjNdGpF7n>y+RTAZc9&opTXf zqWfK_dUv=mW{p_vN>|(cIkd(+Jy}qnK{IW%X*3!l`^H~FbAHwof+vLZ0C2ZXN1$v7 zgN&R9c8IO`fkR{6U%ERq8FN<1DQYbAN0-pH7EfcA{A&nhT!Be>jj>J!bNRw4NF|}! z1c70_#fkk!VQ!q1h2ff@`yDyrI1`np>*e#D4-Z~*!T^8#o*$V~!8bWQaie?P@KGBb z8rXc!YDL!$3ZgZZ%;-%~0Kn<+d+{xJ$stQbtN8GWV?MCJvzPU|(E(1z;rFw{&6vy) z3*@y%7Tx8rH-p$boS>bLyod?OKRE8v`QSBvGfY6f}_{Zo1q85xoyOF16n~yHx2W ziydUoYLkJmzq|n&2S(O!ZmLdP1(o1Jsq88cX)x3V-BK5eF&0e_0G!5?U7&3KN0`mc zH&Lt)q8!d_VgzxyL^(@xrbp2y)Hmr^V48));RSfE=*Ly0uh9!$3dv-vMZr2URf@l5zdwLjGZB zugY>7_fd_vbV*Qv1?H~>Z%RD%nEeFSI$n$$Lrpc6g>i4+XdBB!%zM$Bhrz5Swzyg? z$~I~n@~-wTBY3-T&pr+|gC+OHDoR?I(eLWa{Z#Rsh>lc~%u0!&R|s0pA*w<7QZ}{i z*AFr~0F3y~f$MGh_HDL7J_1?SxKL}fWIk!$G}`^{)xh*dZ5kK>xGL9>V`WZZg_ z)^Vm)EQK`yfh5KiR(vb&aHvhich z_5o+{d~0+4BEBqYJXyXBIEb1UgVDs;a!N2$9WA>CbfrWryqT25)S4E4)QXBd*3jN} z?phkAt`1rKW?xoLzEm!*IfkH|P>BtECVr0l8-IGk_`UjE#IWkUGqvyS+dMrCnFl<7RCgSMX^qn|Ld_4iYRldO zY&cHhv)GDo8nKvKwAbfyLR%t?9gG?R7~PSD#4D-;?F&!kV59O}neYut5AGbKwy-(U zqyBi=&Mgj|VIo>$u!DHM`R7O?W8-idbePuxiJMH``6c_5L-chKd}=rGC5Gfrc{f!* zWFEBm?l@_b7kzY7%1RQQbG5V<4=ZlkZ%sF74Q|mKOc7Ak7dP2#quiGcZ0_J%7Q?j{ zv9{WFw;n5G-Mn%r#0R;{jLt{yy}9J6rQ(>X9pJ`7Xy?Zv z=lNit#qXaq?CnElK^zF~sG}U5oCpR0T>FH=ZX}Prju$);?;VOhFH8L3I><9P_A|C+ z{;>~dk%9rrq(snjsEm}oUz2FQ21MCG*e?g)?{!&|eg7PX@I+Q0!hL6C7ZVY|g2E>i zr!Ri2@OfEu$)d52+>+cpgh6Z;cLYCZ&EMR0i<^~4&wEu_bdo;y^6}+U2GIQgW$|Od z_jg{O=pU>0-H$P-EOlWyQy#W0r@@_uT}Lg+!d5NxMii7aT1=|qm6BRaWOf{Pws54v zTu=}LR!V(JzI07>QR;;px0+zq=(s+XH-0~rVbmGp8<)7G+Jf)UYs<$Dd>-K+4}CsD zS}KYLmkbRvjwBO3PB%2@j(vOpm)!JABH_E7X^f#V-bzifSaKtE)|QrczC1$sC<<*Y z$hY*3E10fYk`2W09gM_U<2>+r^+ro$Bqh-O7uSa)cfPE_<#^O) zF+5V;-8LaCLKdIh3UB@idQZL`0Vx8`OE#6*1<;8(zi&E7MWB1S%~HAm%axyIHN2vd zA(pJGm_PraB0Aat3~?obWBs?iSc*NhM!{-l_WNCx4@F7I?)5&oI|z{o@JKd1HZ}zf*#}JjK3$ z-;3V*WJZvUcKvSOBH4c7C{fl8oRw8-vfgKQjNiR|KhQ%k6hWNEke(k8w-Ro| z7Y3)FsY-?7%;VT64vRM)l0%&HI~BXkSAOV#F3Bf#|3QLZM%6C{paqLTb3MU-_)`{R zRdfVQ)uX90VCa3ja$8m;cdtxQ*(tNjIfVb%#TCJWeH?o4RY#LWpyZBJHR| z6G-!4W5O^Z8U}e5GfZ!_M{B``ve{r0Z#CXV0x@~X#Pc;}{{ClY_uw^=wWurj0RKnoFzeY` z;gS!PCLCo*c}-hLc?C&wv&>P1hH75=p#;D3{Q8UZ0ctX!b)_@Ur=WCMEuz>pTs$@s z#7bIutL9Pm2FDb~d+H}uBI#pu6R}T{nzpz9U0XLb9lu@=9bTY&PEyFwhHHtXFX~6C zrcg|qqTk(|MIM%KQ<@j=DOjt|V)+8K26wE_CBNnZTg+Z+s}AU|jp6CFoIptG1{J*# z7Ne~l;ba*=bSwAMQ|Vq#fW~+je4PXA91YFzBubNF?ovIOw-$C-8=Ehed{lGD0}(Id zRe4sh8L>&T%{>8o))he}eE;5_ zxoXk3wX?MyNl-xF!q1d$G?=wp^`@09(jU&X zOqZIBI#dN`2PJNdATR3ivtub|nO$dulSaP|e4)WXF1YAGN1pDQIbIjXFG!oC85Mt; zW$eteoL{y^5t4TMRwP$jNPjZFpGsWnGe=jMMqKtcZm9Y9PFZLi*1p@qoKKub^T@2+ zk$@*KYdQ?Z`}<%4ALwk*Yc{(WTf@#u;as(fvE^9{Gk)lWbJP*SjttWofV0s?AB({~l zZI1hZVWFT~W-T?nfMMcnCS4-#6H-MU7H$KxD;yaM46K4Kc@~Q>xzB+QnD_I`b_l3m zo9pRx46b!p?a^&zCDwygqqV3epjs(s0NQI6ARA1n!Yy-qduipxQ& zUAlqRpNjBS+y-ZheD(!R;F}&^V_}b_gqH%tVZ5%%ziO7k^w=es+wZtK^i*vmrWNLMs{oWu_CIov|s1raZiS)>38>pYu;i+-t zI_DiNe6aA4KTZ2P09qPj(0~K4nUq^0+f(2$g`229zkG4jLzRvJUWE0oF1XHL4t3UN zDH466G56sy9hTZoAJB!C3;@F;ONxEk5u6Mv%zdo}Rq`=* zw1n7MOhfNSV48TS989ArIcj`C%Gk8~93~u>)!Yt2b4ZriKj9x2d`H2HQNJ=I>hkDlcZn zqRj>!;oRMTIOu zx|Zfsu~v76T{z7AC(jxj^c@tnJHZtGPsq$DE!8kqvkDx5W?KUJPL+!Ffpwfa+|5z5 zKPCiOPqZZrAG;2%OH0T$W|`C@C*!Z`@Wkop{CTjB&Tk`+{XPnt`ND`Haz;xV`H^RS zyXYtw@WlqTvToi;=mq1<-|IQ(gcOpU%)b#_46|IuWL#4$oYLbqwuk6=Q@xZaJSKVF zZcHs~ZBl;&lF3=+nK; zF`4gSCeZXlwmC_t4I`#PUNQ*)Uv&oGxMALip|sxv^lyVV73tKI7)+QY5=tEMas{vTD-BaTJ^*Y6gq~PU;F5X!sxqiq$iFCo+Uv7m%1w((=e}Vf*=dtds|6 zbX}91!G?C*KG03eHoN}RZS9DJxa&8YwNCT8?JxMXyZqZr13NA|GB{+vG`08C{V(yy zf*Lw$+tYSU_+dI`3n{bMrPdDb`A=Mkg!O=k>1|*3MC8j~- zXL79J4E=U^H=iBLTeHE_OKzE&dws8RNynsSJ!d;`zK?P92U{f)xvD7VQVosrXZrL+ z6lMVdD1YgL;%(1cq{#bS6yXmp|DS@nax#AqqlZhtUQdh<^2vr5`EpAO

LGYq)sa(w9^3-f}NHy=GR4v%t2YZly3m1G@5y`xBh_HGrD%f z>;|Ty?9FiJAc&UVD(StT4I` zfVQwxhE9bXE6r2mKO8Ag7{L^jCyqQb0QqKDPE=RAgqn8q1O^>(z7h5kE(6va%QqRZ zkIOmp(})rLSS(2{=C12e&@!W2=Jel-^_R``0xHO^+t!(oXbcv5yhD4g*$t_F)_5Dl zSVCgesW%;DtYPCFs{G;GX_o?1J3;QQPPv)rWw;>} zJ&KwnUqwNXloNXlK_+pNDfI~hON#SokVJb&ilg8d7^NWo2ZQymCqQMnjfi>ePibjr z-Z@q!?RGN$Mj}Nk){X_vaj6?Mj$>ACR*z|6MsXy3VZ^PFn@yHkPo(>m(iWepn8SC@ z>D2;R4m+gDRZ=SIX!b+CP(qE=JDIUkn=D$aUu+Ihn9-+k1LS3PreQg0N5eWIG@x${nC3v^7caS>1!PKNAY9J z#}E}Q9w#SP>(GY7Hbj&z4$Li6o5taBO|4+F`yS9zq*LJ<38wy4I>HA9(&GYrk4dLajKGww))BWli6Ln1A^Lda@N~p+snkb9C z@OthI+<##vp8!HVQT4Wk(=@zQ{OvZ$EKWS73+JHb)eYLGD-cqi6^|vd$<+IHuc?Nq zW7JertT~3))4?J|28n$I@nAD0c1%9C&IVhEZX~mUsf{efyS(XNG%ch;!N~d7S(Ri7 zb&=BuON95aVA&kLn6&MVU|x}xPMp7xwWxNU1wS+F6#y}1@^wQZB*(&ecT?RnQcI}Y z2*z!^!D?gDUhc@;M^OpLs4mq>C&p{}OWVv<)S9KMars@0JQ{c_ScGsFo3BJ)Irg++ zAWwypJdTO-_{Uh8m(Z!3KL7K{ZZzKHj;{M8I$mV>k znTM?sa0);^=X^cglL`uC+^J)M7nEa$w=VwFULg~%DJllw+7dJAj3{qnP5i3@wr7%y zjXp?Wl2%Th=my&3u?Q$RV6N5tzKMSPTsc#J+-cDDp~qFB6bL2C8AS7Y3PKtVhdhl) zIaLqH5+OnWPWSt(lQCgkN8lczc-V%_iZ{>#1%Z$N*>lu#S;0MZ$T2Y8Kg!U;hAZj> z6S#%$DQ_`Ic%Zr@?}GgjRXg@qTj^17n`65oJ@Wj0u1X8&+UVd|Xs?J+i_^GZ94m6= zUc96~Q`OJvlKB_Lr15*Yw_PUPEr?f?H&00b^-W%26mD)(n(rGGNfK9~2h=C>p-7BZ zFd&*&Msdu{w~(eyFOglwCPH^Rb}O(N7LtS+nnEwDx*pGD?|&9Si~M43a+*L(b0$5A zv`T`(G3xO;I_sx;FwTP21ZlfDpz zOo?}Vlgf~fo{YWm@n_JyD*frOg{XsvBA~|Tn4V6hu>Gd>89-rblfVJUaGvj6X%NZ} z$tFF9sx=4_$*c~G`9iPLGh@=sV+O{D2-t*K@J7H=`V+oVt}8?04WwU3h1BgS!f%1P zFak-T#7`TtLcR=Yz>g0R!ZQrH!YiZOQN=_V-UyncN1Rc18?KY?#O`v#JK+pq0K$~H z3D@v9DZF42R)b9#BBX{^$DOMlJ!g)Gc za{o-1e%F6NvgKq9tC8pV+9S$;9*zNv{J*)n&dmf~anP1)4~N%~h#c(=B#3*KgzhCKhFdgDoWi2IDog{RVyzK|Y`rCUs3T~pJMmdZJy4?b z&s5G=zhf**(t7Y^oC_mcTsE-{^}wiaoUu&?kojLKs>SJPxjcP>{a5CbXCx92AcBE) zHtqP}LjZ{W>PH?Tu(E0X=%{PBMW@F_?#7b&#!^q`<-5$ur+-q6 z{dn=(^UZw6*3-XM_(=@<1_*i&XM4=0t5u!gm6 z{UlmNGPKgO_;e;q9|#esq~Sq`<}%d{+sRmhvsA{5i*91=tub>OZZ%)xUA#4q$dDyy z1`w4%?OPLg3JeZb#cqSMO?*Xn%|-FCcuH2i2fn_{IFusub6;NQdN|7TD1N?%E8*g? z$apAt@cEe!I%jB=*q$p_3=t_5R0ph%{qaq+QDg!c99Y!Xa!&oDZOeis_ot)gNXr{l zdY$|So2Qed2Y7KMNBrS^E169kG%h<+z{Z_p_;shB!uY)>yAVcK=&!bg`lVg)4T1|7 z0}7FpfydVH4F87K@c!nEG+WGKm{Ouo)Slpl;#qcEIQ0zdMfLA#;dBxYw;p;KoVv6| z3_D5&7rJdG12CnDSvZUW?$UC6^UVSW^|vw|o-_4bz)(w5(3AiVhpeT(|=f#x_}E?s#qHZF#xA6AF_ujl$G z-jHD%q(d2}v2PhXx&6YWps~m(^+RXl91Q#xRRJBhjKl$FG4bk);|ag;ieUZ&!Ii3$ z(iGz1+0m7#g5>ASldBbNZL=ZHh=tmmJt$!71; zIML2GhEz1pg@1rQN(M^_691wAGkJ@Pga_05WuQ6! zG5RkGY2^`@(H~pp7&Ga+Pwh3L!Njj!-rc;^bTIfo5hP@H##1X8xUZJckrx>id`bAd3QUx9GuomqBYZ!uN1-&o zvTxC?;p8vL67&fW8fw(YOqt>L@bdLrEF*3OgYe$4n4{ zEB40LiU#6-0@5jdN`0w}N0qi@c0~oT2FP z)LNk&a82my?jv(tQpiMi$TK_L@lub#lsM$R{Dk?Ya@%%%huZkct~tSWM714c!45k}-ZLVA-bVM`>|_ZBbW_m-7| z3U%xrAhi}n?T(2F{_n4EZ10inkIFl#y09?7$uwBoJgqY8vylwev)fDOn;>0R!aEnV zBz%j0Mqpx~EZU3q@%+oV7;}|vt7$~ou@faEIq{p?FY$XXg&6*K)b_LP=}gi9`Bij3 zN`zEo|B6*|-;>S`rNa^BKRDbDAk>X#MsR`EvL>6bqU@SaDDs z8>bu@3YdRaWs*Te@G-UHjU%F~kTHw5(0PVJ+pwh#ha2u;DB+UMo@A5UYIl#5rtBV- zGX_hIpw}3C@H*Us(Cc-d#-gNrG#w$(9+S=GxO>3SR`SE2fHZ2KrDc#_C^$jI>Y}#; zMwY=R6@+dWi~0RXw(c@3GZ&%~9K(q&ee0Zw;pwL`E_tZak-#8^_b)Dpyi73^he?xV zXJ08&wh5-M&}qy4f7!D&=E)puDD(Nmg1d_(j`4LvxM5x_huNg-pGG%9rYqO6mImyJ@}*3Y>^3OvcnTG%EV1) zq_Ap?Z!Iw__7#D=pOWnQN$gB!Mr0!9yx|g<4icJh{cFOu3B8}&RiYm+Mb;VEK``LK zL(NcpcTiGieOIssSjr?ob}^``nNf&UcJhXyncO9m{6gD$kqSD`S69(aF8dkWz5>!9 zBLe4Sib7Hs2x_L2Ls6Ish$MGVKrGt5+_2zCyP1byaCg3upo+-I}R4&$m)8 zQ7|jc1Z^VWggpuQj*cP;>Zo9LS!VSzrqmZczaf;u`d0J(f%Z9r%An@s!e>n9%y=n!IZ_tVGu{Jmsbp}Fk%HJIU?a+-~bjfLTuH|JExA8EROowzr zqW9{YyZhR0a4clRK>1I4Ncx&WER~{iE;F^$T7K%X@3PGOA%6#Z%p3TS^&M;Dnjw@i z^o!$9nhcsmcHcY4?4j9+ofL_CWsZ4Hcch(rjsGfGD(nsH>w}^ERqGnz%iGj0j{g}h z7wMkJ-2Z2~eS>2!i}0~B63i;>SyFJU2+>VCS^AxaDOx%g6-t0eM^P<3+*z`ztvOqrG3)&#$K?& z_Y0wbWID47@cU`E1A6A&!`aZk0ZE@z-h#l1NqX2#`$Uev2gepW`rf8*!=rD5&;Jb{ zl08rU>dPo=K%-1Ao1~G-@4ve~y5#9E8x;TE0k5d^TC(=Zc>mwjW^c=+U-<9}b0ku~}gj z3sbW>R2M6DR!g#NUP;nxo>)@7*=RP{U18SDop6b2&PHce^&h97@xx3t+VK+!keE#} z;(Uf&89as9k8{$nkLbuB!-d7TP`_VJpL^Xs8OKB~ri$YUbW8fch64}7|0EWoT(TRj{ z*GT<7Y<7DsrCi79ZsM)z#c(!nNOGySOCkY1fAuQOq12&iUVC!a`#O;dBLf=d?&4*B zI~LgAO7E0qxK(uRTM;IgJ}+z^gD+bi-6I!3x{r9`l~%8TRP%UE0V8E*Sz>Nl1NVG<<7(wDHZ+HcOkQm$O&k+vyx)y)x{Pz!U8hS$*m zByc0h6BUI*BOpuL==P+H|Hx%`>7!W+1H!l9vi&)`V zyn2o9{z=lc+VX*!Vh~SF=)L}Z40XeG>LF6cP^b+R$NxSeUqbK^Q*UTalKzP8X%{9@RSCXm_NhF>{=S2 zi}ezam_^P`S!!-cyEW9y7DBbK93roz@Raccy*v}?mKXScU9E_4g;hBU7}zSofAFda zKYEe?{{I54 From 9ae475ab28247f646e79426a0e4eafaa521ee28a Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 16 Oct 2024 16:12:29 -0400 Subject: [PATCH 2/9] chore: Rewrite core to use koin module tests: Ensure all core tests passing --- addons/geary-actions/build.gradle.kts | 7 +- .../actions/ConfigEntityObserversTests.kt | 40 ++--- addons/geary-autoscan/build.gradle.kts | 1 - .../geary/autoscan/AutoScannerDSL.kt | 3 +- addons/geary-prefabs/build.gradle.kts | 4 - .../mineinabyss/geary/prefabs/PrefabLoader.kt | 2 +- ...erTest.kt => ComponentIdSerializerTest.kt} | 2 +- ...> GearyEntityComponentIdSerializerTest.kt} | 2 +- addons/geary-serialization/build.gradle.kts | 3 - .../geary/serialization/FileSystemAddon.kt | 9 +- .../serializers/SerializableComponentId.kt | 38 ++-- addons/geary-uuid/build.gradle.kts | 2 - .../mineinabyss/geary/uuid/UUID2GearyMap.kt | 4 +- .../geary/uuid/systems/TrackUuidOnAdd.kt | 5 +- .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 2 +- build.gradle.kts | 1 + .../geary/benchmarks/events/EventCalls.kt | 4 +- geary-core/build.gradle.kts | 8 +- .../geary/addons/dsl/GearyAddon.kt | 22 +-- .../geary/components/ReservedComponents.kt | 22 +++ .../com/mineinabyss/geary/datatypes/Entity.kt | 104 ++++++----- .../geary/datatypes/EntityIdArray.kt | 11 +- .../mineinabyss/geary/datatypes/Relation.kt | 9 - .../geary/datatypes/family/MutableFamily.kt | 51 +++--- .../datatypes/maps/Family2ObjectArrayMap.kt | 16 +- .../geary/engine/ComponentProvider.kt | 1 - .../geary/engine/archetypes/Archetype.kt | 169 ++++-------------- .../engine/archetypes/ArchetypeEngine.kt | 3 +- .../archetypes/ArchetypeQueryManager.kt | 12 +- .../archetypes/ComponentAsEntityProvider.kt | 35 ++-- .../archetypes/EntityByArchetypeProvider.kt | 33 ++-- .../archetypes/SimpleArchetypeProvider.kt | 10 -- .../operations/ArchetypeMutateOperations.kt | 133 ++++++++++++-- .../operations/ArchetypeReadOperations.kt | 3 - .../geary/helpers/ArchetypeHelpers.kt | 8 - .../geary/helpers/ComponentHelpers.kt | 2 +- .../geary/helpers/EngineHelpers.kt | 10 +- .../geary/helpers/EntityHelpers.kt | 11 -- .../mineinabyss/geary/helpers/Relationship.kt | 6 +- .../modules/ArchetypeEngineInitializer.kt | 19 ++ .../geary/modules/ArchetypeEngineModule.kt | 127 +++++++------ .../geary/modules/EngineInitializer.kt | 6 + .../com/mineinabyss/geary/modules/Geary.kt | 144 +++++++++++---- .../geary/modules/GearyConfiguration.kt | 70 -------- .../mineinabyss/geary/modules/GearyModule.kt | 76 +++----- .../geary/modules/GearyModuleProvider.kt | 6 - .../modules/GearyModuleProviderWithDefault.kt | 5 - .../mineinabyss/geary/modules/GearySetup.kt | 12 +- .../geary/modules/MutableAddons.kt | 2 +- .../geary/modules/TestEngineModule.kt | 31 +--- .../geary/observers/ArchetypeEventRunner.kt | 3 +- .../observers/builders/ObserverBuilder.kt | 3 +- .../builders/ObserverEventsBuilder.kt | 3 +- .../geary/observers/entity/EntityObserver.kt | 2 +- .../observers/queries/CacheQueryAsMap.kt | 1 + .../systems/accessors/AccessorOperations.kt | 17 +- .../systems/accessors/RelationWithData.kt | 2 - .../accessors/type/ComponentAccessor.kt | 4 +- .../accessors/type/RelationsAccessor.kt | 5 +- .../type/RelationsWithDataAccessor.kt | 5 +- .../geary/systems/builders/GlobalFunctions.kt | 8 - .../geary/systems/query/CachedQuery.kt | 4 +- .../geary/systems/query/QueriedEntity.kt | 10 +- .../geary/systems/query/QueryShorthands.kt | 2 +- .../ComponentAsEntityProviderTest.kt | 15 ++ .../geary/datatypes/EntityTypeTest.kt | 2 +- .../datatypes/Family2ObjectArrayMapTest.kt | 31 ++-- .../mineinabyss/geary/datatypes/FamilyTest.kt | 24 +-- .../geary/datatypes/GearyEntityTests.kt | 23 +-- .../mineinabyss/geary/engine/ArchetypeTest.kt | 19 +- .../geary/engine/GearyEngineTest.kt | 6 +- .../helpers/GearyEntityWithOperatorTest.kt | 2 +- .../geary/helpers/GearyTestTest.kt | 19 +- .../helpers/GearyTypeFamilyHelpersTest.kt | 2 +- .../geary/helpers/tests/GearyTest.kt | 42 +---- .../geary/instancing/InstancingTest.kt | 11 +- .../geary/koin/ArchetypeEngineModuleCheck.kt | 40 +++++ .../observers/EntityRemoveObserverTest.kt | 9 +- .../observers/ObserveComponentEventsTests.kt | 25 ++- .../geary/observers/ObserverTypeTests.kt | 12 +- .../geary/observers/QueryAsMapTest.kt | 4 +- .../entity_scoped/EntityScopedObserverTest.kt | 7 +- .../geary/queries/SimpleQueryTest.kt | 18 +- .../AccessorDataModificationTests.kt | 7 +- .../queries/accessors/MappedAccessorTests.kt | 8 +- .../geary/systems/FamilyMatchingTest.kt | 11 +- .../systems/RelationMatchingSystemTest.kt | 20 ++- geary-test/build.gradle.kts | 13 ++ .../com/mineinabyss/geary/test/GearyTest.kt | 58 ++++++ gradle.properties | 2 +- gradle/libs.versions.toml | 7 +- settings.gradle.kts | 3 +- 92 files changed, 927 insertions(+), 888 deletions(-) rename addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/{SerializerTest.kt => ComponentIdSerializerTest.kt} (98%) rename addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/{GearyEntitySerializerTest.kt => GearyEntityComponentIdSerializerTest.kt} (97%) create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/components/ReservedComponents.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineInitializer.kt create mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/EngineInitializer.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProvider.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProviderWithDefault.kt delete mode 100644 geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt create mode 100644 geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/components/ComponentAsEntityProviderTest.kt create mode 100644 geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/koin/ArchetypeEngineModuleCheck.kt create mode 100644 geary-test/build.gradle.kts create mode 100644 geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt diff --git a/addons/geary-actions/build.gradle.kts b/addons/geary-actions/build.gradle.kts index f57f158e5..959238cd0 100644 --- a/addons/geary-actions/build.gradle.kts +++ b/addons/geary-actions/build.gradle.kts @@ -10,19 +10,16 @@ kotlin { dependencies { implementation(project(":geary-core")) implementation(project(":geary-serialization")) - - implementation(libs.uuid) - implementation(idofrontLibs.idofront.di) } } - val commonTest by getting { + val jvmTest by getting { dependencies { + implementation(project(":geary-test")) implementation(kotlin("test")) implementation(idofrontLibs.kotlinx.coroutines.test) implementation(idofrontLibs.kotlinx.serialization.kaml) implementation(idofrontLibs.kotest.assertions) implementation(idofrontLibs.kotest.property) - implementation(idofrontLibs.idofront.di) implementation(project(":geary-core")) implementation(project(":geary-serialization")) } diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt index a4cfe77d8..c7021443e 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt @@ -3,22 +3,20 @@ package com.mineinabyss.geary.actions import com.mineinabyss.geary.actions.event_binds.EntityObservers import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.serialization.dsl.serialization +import com.mineinabyss.geary.modules.observeWithData +import com.mineinabyss.geary.serialization.SerializableComponents import com.mineinabyss.geary.serialization.dsl.withCommonComponentNames import com.mineinabyss.geary.serialization.formats.YamlFormat -import com.mineinabyss.geary.serialization.serializableComponents import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer -import com.mineinabyss.geary.systems.builders.observeWithData -import com.mineinabyss.idofront.di.DI import io.kotest.matchers.shouldBe import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import test.GearyTest -class ConfigEntityObserversTests { +class ConfigEntityObserversTests : GearyTest() { @Serializable @SerialName("geary:print") data class Print(val string: String) @@ -27,23 +25,19 @@ class ConfigEntityObserversTests { @SerialName("geary:my_comp") class MyComp() - @BeforeEach - fun createEngine() { - DI.clear() - geary(TestEngineModule) { - install(GearyActions) + override fun setupGeary() = geary(TestEngineModule) { + install(GearyActions) - serialization { - withCommonComponentNames() + serialization { + withCommonComponentNames() - components { - component(String.serializer()) - component(Print.serializer()) - component(EntityObservers.serializer()) - component(MyComp.serializer()) - } + components { + component(String.serializer()) + component(Print.serializer()) + component(EntityObservers.serializer()) + component(MyComp.serializer()) } - }.start() + } } @Test @@ -57,10 +51,10 @@ class ConfigEntityObserversTests { string: "Hello World" """.trimIndent() - val format = YamlFormat(serializableComponents.serializers.module) - val entity = format.decodeFromString(GearyEntitySerializer, entityDef) + val format = YamlFormat(getAddon(SerializableComponents).serializers.module) + val entity = format.decodeFromString(GearyEntitySerializer(this), entityDef) val printed = mutableListOf() - geary.observeWithData().exec { printed += event.string } + observeWithData().exec { printed += event.string } // act entity.set(MyComp()) diff --git a/addons/geary-autoscan/build.gradle.kts b/addons/geary-autoscan/build.gradle.kts index 9d718d8b4..2a0d84dc2 100644 --- a/addons/geary-autoscan/build.gradle.kts +++ b/addons/geary-autoscan/build.gradle.kts @@ -10,5 +10,4 @@ dependencies { implementation(idofrontLibs.reflections) implementation(idofrontLibs.kotlin.reflect) - implementation(idofrontLibs.idofront.di) } diff --git a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt index 2e63d1801..73e820617 100644 --- a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt +++ b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt @@ -3,7 +3,6 @@ package com.mineinabyss.geary.autoscan import co.touchlab.kermit.Severity import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.datatypes.Component -import com.mineinabyss.geary.modules.GearyConfiguration import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.modules.GearySetup import com.mineinabyss.geary.modules.geary @@ -112,7 +111,7 @@ class AutoScannerDSL( fun subClassesOf(kClass: KClass) { geary { serialization { - module { + application { polymorphic(kClass) { val scanned = this@AutoScannerDSL.reflections .get(Scanners.SubTypes.of(kClass.java).asClass>(this@AutoScannerDSL.classLoader)) diff --git a/addons/geary-prefabs/build.gradle.kts b/addons/geary-prefabs/build.gradle.kts index 8bb84b091..445493c2e 100644 --- a/addons/geary-prefabs/build.gradle.kts +++ b/addons/geary-prefabs/build.gradle.kts @@ -10,9 +10,6 @@ kotlin { dependencies { implementation(project(":geary-core")) implementation(project(":geary-serialization")) - - implementation(libs.uuid) - implementation(idofrontLibs.idofront.di) } } @@ -22,7 +19,6 @@ kotlin { implementation(idofrontLibs.kotlinx.coroutines.test) implementation(idofrontLibs.kotest.assertions) implementation(idofrontLibs.kotest.property) - implementation(idofrontLibs.idofront.di) implementation(project(":geary-core")) implementation(project(":geary-serialization")) } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt index 9b4abda0b..821612563 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt @@ -1,6 +1,5 @@ package com.mineinabyss.geary.prefabs -import com.benasher44.uuid.Uuid import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity @@ -19,6 +18,7 @@ import com.mineinabyss.geary.systems.query.Query import kotlinx.serialization.Serializable import kotlinx.serialization.modules.SerializersModule import okio.Path +import kotlin.uuid.Uuid class PrefabLoader { private val formats get() = serializableComponents.formats 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/ComponentIdSerializerTest.kt similarity index 98% rename from addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/SerializerTest.kt rename to addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/ComponentIdSerializerTest.kt index 77b777dcb..675d906ed 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/ComponentIdSerializerTest.kt @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SerializerTest { +class ComponentIdSerializerTest { interface Components @SerialName("test:thing.a") diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntitySerializerTest.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt similarity index 97% rename from addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntitySerializerTest.kt rename to addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt index 82cd0247a..a1db2449f 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntitySerializerTest.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class GearyEntitySerializerTest { +class GearyEntityComponentIdSerializerTest { init { DI.clear() diff --git a/addons/geary-serialization/build.gradle.kts b/addons/geary-serialization/build.gradle.kts index 3252a1e9c..648c0dc7d 100644 --- a/addons/geary-serialization/build.gradle.kts +++ b/addons/geary-serialization/build.gradle.kts @@ -10,9 +10,6 @@ kotlin { dependencies { implementation(project(":geary-core")) - implementation(libs.uuid) - implementation(idofrontLibs.idofront.di) - api(idofrontLibs.kotlinx.serialization.cbor) api(idofrontLibs.kotlinx.serialization.json) api(libs.okio) diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt index f9bc81427..baf5a1a01 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt @@ -1,10 +1,9 @@ package com.mineinabyss.geary.serialization import com.mineinabyss.geary.addons.dsl.createAddon -import com.mineinabyss.idofront.di.DI import okio.FileSystem -val fileSystem by DI.observe() - - -fun FileSystemAddon(fileSystem: FileSystem) = createAddon("File System", { fileSystem }) +val FileSystemAddon = createAddon( + "File System", + { error("No FileSystem passed into addon, please use FileSystemAddon()") } +) diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt index e99a62973..e76d6a93e 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt @@ -3,8 +3,10 @@ package com.mineinabyss.geary.serialization.serializers import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.SerializableComponents +import kotlinx.serialization.Contextual import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.encoding.Decoder @@ -12,26 +14,26 @@ import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.modules.SerializersModule import kotlin.reflect.KClass -@Serializable(with = SerializableComponentId.Serializer::class) -class SerializableComponentId(val id: ComponentId) { - object Serializer : KSerializer { - override val descriptor = PrimitiveSerialDescriptor("EventComponent", PrimitiveKind.STRING) +typealias SerializableComponentId = @Contextual ComponentId - private val polymorphicListAsMapSerializer = PolymorphicListAsMapSerializer.ofComponents() +class ComponentIdSerializer(val world: Geary) : KSerializer { + val serializers = world.getAddon(SerializableComponents).serializers + override val descriptor = PrimitiveSerialDescriptor("EventComponent", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): SerializableComponentId { - return SerializableComponentId(componentId(getComponent(decoder.decodeString(), decoder.serializersModule))) - } + private val polymorphicListAsMapSerializer = PolymorphicListAsMapSerializer.ofComponents() - override fun serialize(encoder: Encoder, value: SerializableComponentId) { - TODO() - } + override fun deserialize(decoder: Decoder): SerializableComponentId { + return world.componentId(getComponent(decoder.decodeString(), decoder.serializersModule)) + } + + override fun serialize(encoder: Encoder, value: SerializableComponentId) { + TODO() + } - fun getComponent(name: String, module: SerializersModule): KClass { - val namespaces = polymorphicListAsMapSerializer - .getParentConfig(module)?.namespaces - ?: emptyList() - return serializableComponents.serializers.getClassFor(name, namespaces) - } + fun getComponent(name: String, module: SerializersModule): KClass { + val namespaces = polymorphicListAsMapSerializer + .getParentConfig(module)?.namespaces + ?: emptyList() + return serializers.getClassFor(name, namespaces) } } diff --git a/addons/geary-uuid/build.gradle.kts b/addons/geary-uuid/build.gradle.kts index 3ef506709..a342a5c64 100644 --- a/addons/geary-uuid/build.gradle.kts +++ b/addons/geary-uuid/build.gradle.kts @@ -9,9 +9,7 @@ kotlin { dependencies { implementation(project(":geary-core")) - api(libs.uuid) implementation(libs.atomicfu) - implementation(idofrontLibs.idofront.di) } } } diff --git a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt index 40dd81e2e..51b6b42e6 100644 --- a/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt +++ b/addons/geary-uuid/src/commonMain/kotlin/com/mineinabyss/geary/uuid/UUID2GearyMap.kt @@ -1,11 +1,9 @@ package com.mineinabyss.geary.uuid -import com.benasher44.uuid.Uuid import com.mineinabyss.geary.datatypes.EntityId -import com.mineinabyss.geary.datatypes.GearyEntity -import com.mineinabyss.geary.helpers.toGeary import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.synchronized +import kotlin.uuid.Uuid interface UUID2GearyMap { operator fun get(uuid: Uuid): EntityId? 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 56c63c452..cbc647381 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 @@ -1,18 +1,17 @@ package com.mineinabyss.geary.uuid.systems -import com.benasher44.uuid.Uuid -import com.benasher44.uuid.uuid4 import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.systems.query.query import com.mineinabyss.geary.uuid.UUID2GearyMap import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash +import kotlin.uuid.Uuid fun Geary.trackUUIDOnAdd(uuid2Geary: UUID2GearyMap) = observe().involving(query()).exec { (uuid) -> val regenerateUUIDOnClash = entity.get() if (uuid in uuid2Geary) if (regenerateUUIDOnClash != null) { - val newUuid = uuid4() + val newUuid = Uuid.random() entity.set(newUuid) uuid2Geary[newUuid] = entity.id } else error("Tried tracking entity $entity with already existing uuid $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 4b015c28f..765d9c037 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,10 +1,10 @@ package com.mineinabyss.geary.uuid.systems -import com.benasher44.uuid.Uuid import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.events.OnRemove import com.mineinabyss.geary.systems.query.query import com.mineinabyss.geary.uuid.UUID2GearyMap +import kotlin.uuid.Uuid fun Geary.untrackUuidOnRemove(uuid2Geary: UUID2GearyMap) = observe() .involving(query()).exec { (uuid) -> uuid2Geary.remove(uuid) } diff --git a/build.gradle.kts b/build.gradle.kts index c1ddb768e..aeabfb97a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,6 +41,7 @@ allprojects { optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") optIn("kotlin.time.ExperimentalTime") optIn("kotlin.ExperimentalUnsignedTypes") + optIn("kotlin.uuid.ExperimentalUuidApi") optIn("kotlinx.serialization.ExperimentalSerializationApi") optIn("kotlin.RequiresOptIn") } 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 097b29ac0..21889f866 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 @@ -3,6 +3,7 @@ package com.mineinabyss.geary.benchmarks.events import com.mineinabyss.geary.benchmarks.helpers.oneMil import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.builders.observe @@ -18,7 +19,7 @@ class EventCalls { @Setup(Level.Invocation) fun setupPerInvocation() { geary(TestEngineModule) - targets = (1..oneMil).map { entity { set(it) } } + targets = (1..oneMil).map { entity().apply { set(it) } } createListener() } @@ -39,6 +40,7 @@ class EventCalls { targets[it].emit() } } + } fun main() { diff --git a/geary-core/build.gradle.kts b/geary-core/build.gradle.kts index d541a01e6..2cbf1f3a0 100644 --- a/geary-core/build.gradle.kts +++ b/geary-core/build.gradle.kts @@ -12,19 +12,21 @@ kotlin { implementation(libs.androidx.collection) implementation(idofrontLibs.kotlin.reflect) - api(idofrontLibs.idofront.di) + api(libs.koin.core) api(idofrontLibs.kermit) api(idofrontLibs.kotlinx.coroutines) } } - val commonTest by getting { + + val jvmTest by getting { dependencies { + implementation(project(":geary-test")) implementation(kotlin("test")) implementation(idofrontLibs.kotlinx.coroutines.test) implementation(idofrontLibs.kotest.assertions) implementation(idofrontLibs.kotest.property) - implementation(idofrontLibs.idofront.di) + implementation(libs.koin.test) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index bc17da72a..a3189410e 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -1,9 +1,11 @@ package com.mineinabyss.geary.addons.dsl +import co.touchlab.kermit.Logger import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.modules.Geary -import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.idofront.di.DIContext +import org.koin.core.KoinApplication +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import kotlin.jvm.JvmName interface GearyAddonWithDefault : GearyAddon { @@ -27,10 +29,10 @@ data class Addon( data class AddonSetup( val name: String, val configuration: Configuration, - val module: GearyModule, - val context: DIContext, -) { - val geary: Geary = Geary(module, context, module.logger.withTag(name)) + val application: KoinApplication, +): KoinComponent { + val logger = get().withTag(name) + val geary: Geary = Geary(application, logger) /** Runs a block during [GearyPhase.INIT_COMPONENTS] */ fun components(configure: Geary.() -> Unit) { @@ -71,7 +73,7 @@ data class AddonSetup( * ``` */ fun on(phase: GearyPhase, run: () -> Unit) { - module.pipeline.runOnOrAfter(phase, run) + geary.pipeline.runOnOrAfter(phase, run) } } @@ -79,7 +81,7 @@ fun createAddon( name: String, init: AddonSetup.() -> Unit = {}, ): Addon = Addon(name, { }) { - init(AddonSetup(name, it, module, context)) + init(AddonSetup(name, it, application)) } @JvmName("createAddon1") @@ -88,7 +90,7 @@ fun createAddon( configuration: Geary.() -> Conf, init: AddonSetup.() -> Unit = {}, ): Addon = Addon(name, configuration) { conf -> - init(AddonSetup(name, conf, module, context)) + init(AddonSetup(name, conf, application)) conf } @@ -99,6 +101,6 @@ fun createAddon( init: AddonSetup.() -> Inst, ): Addon { return Addon(name, configuration) { - init(AddonSetup(name, it, module, context)) + init(AddonSetup(name, it, application)) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/components/ReservedComponents.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/components/ReservedComponents.kt new file mode 100644 index 000000000..9f2f56d94 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/components/ReservedComponents.kt @@ -0,0 +1,22 @@ +package com.mineinabyss.geary.components + +import com.mineinabyss.geary.components.relations.ChildOf +import com.mineinabyss.geary.datatypes.entityTypeOf + +/** + * Entity id references that are used internally [EntityProvider] should ensure to sequentially create entities to + * account for these. As a result, these ids should be sequential, starting at zero. + * + * Keeping these as const vals helps improve performance in tight loops that might be referencing these often. + */ +object ReservedComponents { + const val COMPONENT_INFO = 0uL + const val ANY = 1uL + const val CHILD_OF = 2uL + + val reservedComponents = mapOf( + ComponentInfo::class to COMPONENT_INFO, + Any::class to ANY, + ChildOf::class to CHILD_OF, + ) +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt index 96869ca53..8fa017d71 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Entity.kt @@ -5,11 +5,13 @@ import com.mineinabyss.geary.components.EntityName import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.Engine import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.modules.ArchetypeEngineModule import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.relationOf import com.mineinabyss.geary.observers.events.OnAdd import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.RelationWithData +import org.koin.core.Koin +import org.koin.core.KoinApplication import kotlin.reflect.KClass typealias GearyEntity = Entity @@ -21,42 +23,34 @@ typealias GearyEntity = Entity */ //TODO add require checks for entities across different worlds class Entity(val id: EntityId, val world: Geary) { - inline val idL get() = id.toLong() - - val geary get() = world.module - private val entityProvider get() = world.module.entityProvider - private val entityRemoveProvider get() = world.module.entityRemoveProvider - private val queryManager get() = world.module.queryManager - private val read get() = world.module.read - private val write get() = world.module.write - private val records get() = (world.module as ArchetypeEngineModule).records + val comp get() = world.components /** * Gets this entity's type (the ids of components added to it) * or throws an error if it is no longer active on the koinGet(). */ - val type: EntityType get() = records.getType(id) + val type: EntityType get() = world.records.getType(id) val children: EntityArray - get() = queryManager.getEntitiesMatching(world.family { - hasRelation(geary.components.childOf, this@Entity.id) + get() = world.queryManager.getEntitiesMatching(family { + hasRelation(comp.childOf, this@Entity.id) }).toEntityArray(world) val instances: EntityArray - get() = queryManager.getEntitiesMatching(world.family { - hasRelation(geary.components.instanceOf, this@Entity.id) + get() = world.queryManager.getEntitiesMatching(family { + hasRelation(comp.instanceOf, this@Entity.id) }).toEntityArray(world) - val prefabs: List - get() = getRelations(geary.components.instanceOf, geary.components.any).map { it.target.toGeary(world) } + val prefabs: EntityArray + get() = getRelations(comp.instanceOf, comp.any).map { it.target }.toULongArray().toEntityArray(world) /** Remove this entity from the ECS. */ fun removeEntity() { - entityRemoveProvider.remove(id) + world.entityRemoveProvider.remove(id) } /** Checks whether this entity has not been removed. */ - fun exists(): Boolean = read.exists(id) + fun exists(): Boolean = world.read.exists(id) /** * Sets a component that holds data for this entity @@ -73,7 +67,7 @@ class Entity(val id: EntityId, val world: Geary) { component: Component, componentId: ComponentId, noEvent: Boolean = false, - ): Unit = write.setComponentFor(id, componentId, component, noEvent) + ): Unit = world.write.setComponentFor(id, componentId, component, noEvent) /** Sets components that hold data for this entity */ fun setAll(components: Collection, override: Boolean = true) { @@ -88,7 +82,7 @@ class Entity(val id: EntityId, val world: Geary) { * @param noEvent If true, will not fire an [OnAdd] event. */ fun add(component: ComponentId, noEvent: Boolean = false) { - write.addComponentFor(id, component, noEvent) + world.write.addComponentFor(id, component, noEvent) } /** @@ -123,7 +117,7 @@ class Entity(val id: EntityId, val world: Geary) { /** Removes a component with id [component] from this entity. */ fun remove(component: ComponentId, noEvent: Boolean = false): Boolean = - write.removeComponentFor(id, component, noEvent) + world.write.removeComponentFor(id, component, noEvent) /** * Removes a list of [components] from this entity. @@ -135,7 +129,7 @@ class Entity(val id: EntityId, val world: Geary) { /** Clears all components on this entity. */ fun clear() { - write.clearEntity(id) + world.write.clearEntity(id) } /** Gets a component of type [T] on this entity. */ @@ -147,7 +141,7 @@ class Entity(val id: EntityId, val world: Geary) { /** Gets a [component] which holds data from this entity. Use [has] if the component is not to hold data. */ fun get(component: ComponentId): Component? = - read.get(id, component) + world.read.get(id, component) /** Gets a component of type [T] or sets a [default] if no component was present. */ inline fun getOrSet( @@ -156,13 +150,13 @@ class Entity(val id: EntityId, val world: Geary) { ): T = get(kClass) ?: default().also { set(it) } /** Gets all the components on this entity, as well as relations in the form of [RelationComponent]. */ - fun getAll(): Set = read.getAll(id).toSet() + fun getAll(): Set = world.read.getAll(id).toSet() /** * Checks whether this entity is an instance of another [entity] * (the other is the prefab this entity was made from). */ - fun instanceOf(entity: Entity): Boolean = has(Relation.of(geary.components.instanceOf, entity.id).id) + fun instanceOf(entity: Entity): Boolean = has(Relation.of(comp.instanceOf, entity.id).id) /** Checks whether this entity has a component of type [T], regardless of it holding data. */ inline fun has(): Boolean = has(T::class) @@ -173,7 +167,7 @@ class Entity(val id: EntityId, val world: Geary) { /** Checks whether this entity has a [component], regardless of it holding data. */ fun has(component: ComponentId): Boolean = - read.has(id, component) + world.read.has(id, component) /** * Checks whether an entity has all of [components] set or added. @@ -184,13 +178,14 @@ class Entity(val id: EntityId, val world: Geary) { /** Adds a [base] entity to this entity. */ fun extend(base: Entity) { - require(base.world == world) - write.extendFor(id, base.id) + requireSameWorldAs(base) + world.write.extendFor(id, base.id) } - - /** Adds a [prefab] entity to this entity. */ - fun Entity.removePrefab(prefab: Entity) { - remove(Relation.of(geary.components.instanceOf, prefab.id).id) + + /** Removes a [prefab] from this entity. */ + fun removePrefab(prefab: Entity) { + requireSameWorldAs(prefab) + remove(Relation.of(comp.instanceOf, prefab.id).id) } // Relations @@ -202,33 +197,33 @@ class Entity(val id: EntityId, val world: Geary) { /** Gets the data stored under the relation of kind [K] and target [target]. */ inline fun getRelation(target: Entity): K? { - return get(Relation.of(world, target).id) as? K + return get(world.relationOf(target).id) as? K } /** Like [getRelations], but reads appropriate data as requested and puts it in a [RelationWithData] object. */ @Suppress("UNCHECKED_CAST") // Intrnal logic ensures cast always succeeds inline fun getRelationsWithData(): List> = - geary.read.getRelationsWithDataFor( + world.read.getRelationsWithDataFor( id, world.componentIdWithNullable(), world.componentIdWithNullable() ) as List> fun getRelationsByKind(kind: ComponentId): List = - getRelations(kind, geary.components.any) + getRelations(kind, comp.any) /** Queries for relations using the same format as [AccessorOperations.getRelations]. */ inline fun getRelations(): List = getRelations(world.componentIdWithNullable(), world.componentIdWithNullable()) fun getRelations(kind: ComponentId, target: EntityId): List = - read.getRelationsFor(id, kind, target) + world.read.getRelationsFor(id, kind, target) inline fun hasRelation(): Boolean = hasRelation(world.component()) inline fun hasRelation(target: Entity): Boolean = - has(Relation.of(world, target).id) + has(world.relationOf(target).id) inline fun setRelation(data: K, noEvent: Boolean = false) { setRelation(data, world.component(), noEvent) @@ -239,7 +234,7 @@ class Entity(val id: EntityId, val world: Geary) { } fun setRelation(kind: ComponentId, target: EntityId, data: Component, noEvent: Boolean = false) { - geary.write.setComponentFor(id, Relation.of(kind, target).id, data, noEvent) + world.write.setComponentFor(id, Relation.of(kind, target).id, data, noEvent) } inline fun addRelation(noEvent: Boolean = false) { @@ -247,11 +242,11 @@ class Entity(val id: EntityId, val world: Geary) { } inline fun addRelation(target: Entity, noEvent: Boolean = false) { - geary.write.addComponentFor(id, Relation.of(world, target).id, noEvent) + world.write.addComponentFor(id, world.relationOf(target).id, noEvent) } fun addRelation(kind: ComponentId, target: EntityId, noEvent: Boolean = false) { - geary.write.addComponentFor(id, Relation.of(kind, target).id, noEvent) + world.write.addComponentFor(id, Relation.of(kind, target).id, noEvent) } inline fun removeRelation(noEvent: Boolean = false): Boolean { @@ -259,7 +254,7 @@ class Entity(val id: EntityId, val world: Geary) { } inline fun removeRelation(target: Entity, noEvent: Boolean = false): Boolean { - return geary.write.removeComponentFor(id, Relation.of(world, target).id, noEvent) + return world.write.removeComponentFor(id, world.relationOf(target).id, noEvent) } // Events @@ -268,7 +263,7 @@ class Entity(val id: EntityId, val world: Geary) { } fun emit(event: ComponentId, data: Any? = null, involving: ComponentId = NO_COMPONENT) { - geary.eventRunner.callEvent(event, data, involving, id) + world.eventRunner.callEvent(event, data, involving, id) } // Prefabs @@ -291,7 +286,7 @@ class Entity(val id: EntityId, val world: Geary) { return collectPrefabs(collected, new) } - private tailrec fun deepInstanceOf(seen: MutableSet, search: List, prefab: Entity): Boolean { + private tailrec fun deepInstanceOf(seen: MutableSet, search: EntityArray, prefab: Entity): Boolean { if (search.isEmpty()) return false if (search.any { it.instanceOf(prefab) }) return true seen.addAll(search) @@ -310,7 +305,7 @@ class Entity(val id: EntityId, val world: Geary) { return child?.lookup(remaining) } - operator fun component1(): EntityId = id +// operator fun component1(): EntityId = id // Dangerous operations @@ -329,4 +324,23 @@ class Entity(val id: EntityId, val world: Geary) { fun getOrSet(default: () -> Unit) { getOrSet { } } + + private fun requireSameWorldAs(other: Entity) = require(world.application == other.world.application) { + "Entities must be in the same world to interact with each other. " + + "This entity is in ${world.stringify()}, while the other is in ${other.world.stringify()}" + } + + override fun toString(): String = "Entity($id, world=${world.stringify()})" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Entity) return false + return id == other.id && world.application == other.world.application + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + world.application.hashCode() + return result + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt index bc021ce1a..f73c95f83 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/EntityIdArray.kt @@ -1,7 +1,6 @@ package com.mineinabyss.geary.datatypes import com.mineinabyss.geary.modules.Geary -import kotlin.jvm.JvmInline typealias EntityIdArray = ULongArray @@ -12,7 +11,7 @@ fun EntityIdArray.toEntityArray(world: Geary): EntityArray { class EntityArray( val world: Geary, val ids: EntityIdArray, -): Collection { +) : Collection { override val size: Int get() = ids.size override fun isEmpty(): Boolean = ids.isEmpty() @@ -34,4 +33,12 @@ class EntityArray( inline fun fastForEach(action: (Entity) -> Unit) { for (i in ids.indices) action(GearyEntity(ids[i], world)) } + + inline fun flatMap(transform: (Entity) -> EntityArray): EntityArray { + return ids.flatMapTo(arrayListOf()) { transform(Entity(it, world)).ids }.toULongArray().toEntityArray(world) + } + + operator fun minus(other: Collection): EntityArray { + return ids.minus(other.map { it.id }.toSet()).toULongArray().toEntityArray(world) + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt index 48a751d78..31585189d 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/Relation.kt @@ -46,15 +46,6 @@ value class Relation private constructor( or (target and RELATION_TARGET_MASK) // Add target, stripping any type roles ) - fun of(world: Geary, kind: KClass<*>, target: KClass<*>): Relation = - of(world.componentId(kind), world.componentId(target)) - - inline fun of(world: Geary): Relation = - of(world.componentIdWithNullable(), world.componentId()) - - inline fun of(world: Geary, target: Entity): Relation = - of(world.componentIdWithNullable(), target.id) - /** * Creates a relation from an id that is assumed to be valid. Use this to avoid boxing Relation because of * the nullable type on [toRelation]. 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 28a1755f5..2ce4bb593 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 @@ -1,37 +1,30 @@ package com.mineinabyss.geary.datatypes.family +import com.mineinabyss.geary.components.ReservedComponents import com.mineinabyss.geary.datatypes.* -import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.engine.id import com.mineinabyss.geary.engine.idWithNullable import com.mineinabyss.geary.modules.Geary -inline fun Geary.family(init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { - return family(module.componentProvider, init) -} - -inline fun family(comp: ComponentProvider, init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { - return MutableFamily.Selector.And(comp).apply(init) +inline fun family(init: MutableFamily.Selector.And.() -> Unit): Family.Selector.And { + return MutableFamily.Selector.And().apply(init) } sealed interface MutableFamily : Family { - val comp: ComponentProvider +// val comp: ComponentProvider sealed interface Leaf : MutableFamily { data class Component( - override val comp: ComponentProvider, override var component: ComponentId, ) : Leaf, Family.Leaf.Component data class AnyToTarget( - override val comp: ComponentProvider, override var target: EntityId, override val kindMustHoldData: Boolean, ) : Leaf, Family.Leaf.AnyToTarget data class KindToAny( - override val comp: ComponentProvider, override var kind: ComponentId, override val targetMustHoldData: Boolean, ) : Leaf, Family.Leaf.KindToAny @@ -40,7 +33,6 @@ sealed interface MutableFamily : Family { sealed class Selector : MutableFamily, Family.Selector { class And( - override val comp: ComponentProvider, and: MutableList = mutableListOf(), ) : Selector(), Family.Selector.And { init { @@ -51,7 +43,6 @@ sealed interface MutableFamily : Family { } class AndNot( - override val comp: ComponentProvider, andNot: MutableList = mutableListOf(), ) : Selector(), Family.Selector.AndNot { init { @@ -62,7 +53,6 @@ sealed interface MutableFamily : Family { } class Or( - override val comp: ComponentProvider, or: MutableList = mutableListOf(), ) : Selector(), Family.Selector.Or { init { @@ -94,7 +84,7 @@ sealed interface MutableFamily : Family { } fun has(id: ComponentId) { - add(Leaf.Component(comp, id)) + add(Leaf.Component(id)) } fun hasSet(id: ComponentId) { @@ -106,43 +96,44 @@ sealed interface MutableFamily : Family { kind: ComponentId, target: EntityId, ) { - val specificKind = kind and ENTITY_MASK != comp.types.any - val specificTarget = target and ENTITY_MASK != comp.types.any + val any = ReservedComponents.ANY + val specificKind = kind and ENTITY_MASK != any + val specificTarget = target and ENTITY_MASK != any return when { specificKind && specificTarget -> has(Relation.of(kind, target).id) - specificTarget -> add(Leaf.AnyToTarget(comp, target, kind.holdsData())) - specificKind -> add(Leaf.KindToAny(comp, kind, target.holdsData())) + specificTarget -> add(Leaf.AnyToTarget(target, kind.holdsData())) + specificKind -> add(Leaf.KindToAny(kind, target.holdsData())) else -> error("Has relation check cannot be Any to Any yet.") } } - inline fun hasRelation(): Unit = hasRelation(comp.idWithNullable()) + inline fun Geary.hasRelation(): Unit = hasRelation(componentProvider.idWithNullable()) - inline fun hasRelation(target: EntityId) { - val kind = comp.idWithNullable() + inline fun Geary.hasRelation(target: EntityId) { + val kind = componentProvider.idWithNullable() hasRelation(kind, target) } - inline fun hasRelation(target: Entity): Unit = hasRelation(target.id) + inline fun Geary.hasRelation(target: Entity): Unit = hasRelation(target.id) inline fun or(init: Or.() -> Unit) { - add(Or(comp).apply(init)) + add(Or().apply(init)) } inline fun and(init: And.() -> Unit) { - add(And(comp).apply(init)) + add(And().apply(init)) } inline fun not(init: AndNot.() -> Unit) { - add(AndNot(comp).apply(init)) + add(AndNot().apply(init)) } - inline fun has(): Unit = - has(comp.id()) + inline fun Geary.has(): Unit = + has(componentProvider.id()) - inline fun hasSet(): Unit = - hasSet(comp.id()) + inline fun Geary.hasSet(): Unit = + hasSet(componentProvider.id()) fun has(vararg componentIds: ComponentId) { has(componentIds) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt index ef2879615..fdfd37e70 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/datatypes/maps/Family2ObjectArrayMap.kt @@ -2,16 +2,14 @@ package com.mineinabyss.geary.datatypes.maps import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.family.Family -import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.helpers.hasRelationKind import com.mineinabyss.geary.helpers.hasRelationTarget -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.components.ReservedComponents /** * A map of [ComponentId]s to Arrays of objects with the ability to make fast queries based on component IDs. */ internal class Family2ObjectArrayMap( - val components: Components, val getIndex: ((T) -> Int)? = null, val setIndex: ((T, Int) -> Unit)? = null ) { @@ -48,8 +46,8 @@ internal class Family2ObjectArrayMap( // See componentMap definition for relations if (id.isRelation()) { val relation = Relation.of(id) - set(Relation.of(relation.kind, components.any).id) - set(Relation.of(components.any, relation.target).id) + set(Relation.of(relation.kind, ReservedComponents.ANY).id) + set(Relation.of(ReservedComponents.ANY, relation.target).id) } set(id) } @@ -63,8 +61,8 @@ internal class Family2ObjectArrayMap( // See componentMap definition for relations if (id.isRelation()) { val relation = Relation.of(id) - clear(Relation.of(relation.kind, components.any).id) - clear(Relation.of(components.any, relation.target).id) + clear(Relation.of(relation.kind, ReservedComponents.ANY).id) + clear(Relation.of(ReservedComponents.ANY, relation.target).id) } clear(id) } @@ -122,7 +120,7 @@ internal class Family2ObjectArrayMap( is Family.Leaf.Component -> componentMap[family.component.toLong()]?.copy() ?: bitsOf() is Family.Leaf.AnyToTarget -> { // The bits for relationId in componentMap represent archetypes with any relations containing target - val relationId = Relation.of(components.any, family.target).id + val relationId = Relation.of(ReservedComponents.ANY, family.target).id componentMap[relationId.toLong()]?.copy()?.apply { if (family.kindMustHoldData) forEachBit { index -> val type = elementTypes[index] @@ -134,7 +132,7 @@ internal class Family2ObjectArrayMap( is Family.Leaf.KindToAny -> { // The bits for relationId in componentMap represent archetypes with any relations containing kind - val relationId = Relation.of(family.kind, components.any).id + val relationId = Relation.of(family.kind, ReservedComponents.ANY).id componentMap[relationId.toLong()]?.copy()?.apply { if (family.targetMustHoldData) forEachBit { index -> val type = elementTypes[index] diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt index 549e0328c..3df444378 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/ComponentProvider.kt @@ -8,7 +8,6 @@ import kotlin.reflect.KClassifier import kotlin.reflect.typeOf interface ComponentProvider { - val types: Components /** * Given a component's [kClass], returns its [ComponentId], or registers an entity * with a [ComponentInfo] that will represent this [kClass]'s component type. 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 2981f11cf..f4baf7ff5 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 @@ -1,12 +1,10 @@ package com.mineinabyss.geary.engine.archetypes import androidx.collection.* +import com.mineinabyss.geary.components.ReservedComponents import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -import com.mineinabyss.geary.engine.Components -import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.observers.EventRunner import com.mineinabyss.geary.observers.events.* import com.mineinabyss.geary.systems.accessors.RelationWithData @@ -20,15 +18,11 @@ import com.mineinabyss.geary.systems.accessors.RelationWithData class Archetype internal constructor( val type: EntityType, var id: Int, - private val write: ArchetypeMutateOperations, private val records: ArrayTypeMap, private val archetypeProvider: ArchetypeProvider, - private val eventRunner: EventRunner, - private val comps: Components, - private val queryManager: ArchetypeQueryManager, ) { val entities: EntityIdArray - get() = ULongArray(size) { ids[id].toULong() } + get() = ULongArray(size) { id -> ids[id].toULong() } /** The entity ids in this archetype. Indices are the same as [componentData]'s sub-lists. */ private val ids = mutableLongListOf() @@ -44,7 +38,7 @@ class Archetype internal constructor( /** Component ids in the type that are to hold data */ // Currently all relations must hold data and the HOLDS_DATA bit on them corresponds to the component part. - private val dataHoldingType: EntityType = type.filter { it.holdsData() } + internal val dataHoldingType: EntityType = type.filter { it.holdsData() } /** An outer list with indices for component ids, and sub-lists with data indexed by entity [ids]. */ internal val componentData: Array> = @@ -91,7 +85,7 @@ class Archetype internal constructor( return componentData[compIndex][row] } - private fun getUnsafe(row: Int, componentId: ComponentId): Component { + internal fun getUnsafe(row: Int, componentId: ComponentId): Component { val compIndex = indexOf(componentId) return componentData[compIndex][row] } @@ -211,12 +205,6 @@ class Archetype internal constructor( // For the following few functions, both entity and row are passed to avoid doing several array look-ups // (ex when set calls remove). - internal fun addComponent( - row: Int, - componentId: ComponentId, - callEvent: Boolean, - ) = addComponent(row, componentId, callEvent) { _, _ -> } - /** * Add a [componentId] to an entity represented by [record], moving it to the appropriate archetype. * @@ -225,8 +213,7 @@ class Archetype internal constructor( internal inline fun addComponent( row: Int, componentId: ComponentId, - callEvent: Boolean, - onUpdated: (Archetype, row: Int) -> Unit, + onUpdated: (Archetype, row: Int) -> Unit = { _, _ -> }, ) { // if already present in this archetype, stop here since we don't need to update any data if (contains(componentId)) return @@ -237,17 +224,9 @@ class Archetype internal constructor( val newRow = moveTo.moveOnlyAdding(this, row, entityId) removeEntity(row) - if (callEvent) moveTo.callComponentModifyEvent(comps.onAdd, componentId, newRow, onUpdated) - else onUpdated(moveTo, newRow) + onUpdated(moveTo, newRow) } - internal fun setComponent( - row: Int, - componentId: ComponentId, - data: Component, - callEvent: Boolean, - ) = setComponent(row, componentId, data, callEvent) { _, _ -> } - /** * Sets [data] at a [componentId] for an entity, moving it to the appropriate archetype. * Will ensure this component without [HOLDS_DATA] is always present. @@ -258,8 +237,7 @@ class Archetype internal constructor( row: Int, componentId: ComponentId, data: Component, - callEvent: Boolean, - onUpdated: (Archetype, row: Int) -> Unit, + onUpdated: (firstSet: Boolean, Archetype, row: Int) -> Unit = { _, _, _ -> }, ) { val dataComponent = componentId.withRole(HOLDS_DATA) @@ -267,12 +245,7 @@ class Archetype internal constructor( val addIndex = indexOf(dataComponent) if (addIndex >= 0) { componentData[addIndex][row] = data - if (callEvent) { - callComponentModifyEvent(comps.onUpdate, componentId, row) { arch, row -> - // Potential archetype modification can occur after event - arch.callComponentModifyEvent(comps.onSet, componentId, row, onUpdated) - } - } + onUpdated(false, this, row) return } @@ -283,70 +256,7 @@ 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) { arch, row -> - arch.callComponentModifyEvent(comps.onFirstSet, componentId, row, onUpdated) - } - } else onUpdated(moveTo, newRow) - } - - private inline fun callComponentModifyEvent( - eventType: ComponentId, - involvedComp: ComponentId, - row: Int, - onComplete: (Archetype, row: Int) -> Unit = { _, _ -> }, - ) { - val entity = getEntity(row) - callComponentModifyEvent(eventType, involvedComp, row) - // Don't have any way to know final archetype and row without re-reading - records.runOn(entity, onComplete) - } - - private fun callComponentModifyEvent(eventType: ComponentId, involvedComp: ComponentId, row: Int) { - val entity = getEntity(row) - eventRunner.callEvent(eventType, null, involvedComp, entity) - } - - @Suppress("NAME_SHADOWING") // Want to make sure original arch/row is not accidentally accessed - fun instantiateTo( - baseRow: Int, - instanceArch: Archetype, - instanceRow: Int, - callEvent: Boolean = true, - ) { - val baseEntity = this.getEntity(baseRow) - val instanceEntity = instanceArch.getEntity(instanceRow) - var instanceArch = instanceArch - var instanceRow = instanceRow - instanceArch.addComponent(instanceRow, Relation.of(comps.instanceOf, baseEntity).id, true) { arch, row -> - instanceArch = arch; instanceRow = row - } - - val noInheritComponents = getRelationsByKind(comps.noInherit).map { Relation.of(it).target } - type.filter { !it.holdsData() && it !in noInheritComponents }.forEach { - instanceArch.addComponent(instanceRow, it, true) { arch, row -> instanceArch = arch; instanceRow = row } - } - dataHoldingType.forEach { - if (it.withoutRole(HOLDS_DATA) in noInheritComponents) return@forEach - instanceArch.setComponent(instanceRow, it, getUnsafe(baseRow, it), true) { arch, row -> - instanceArch = arch; instanceRow = row - } - } - queryManager.childrenOf(baseEntity).forEach { child -> - // Add instanceEntity as parent - write.addComponentFor(instanceEntity, comps.couldHaveChildren, true) - write.addComponentFor(child, Relation.of(comps.childOf, instanceEntity).id, false) - } - records.runOn(instanceEntity) { arch, row -> instanceArch = arch; instanceRow = row } - - if (callEvent) { - eventRunner.callEvent( - comps.onExtend, - OnExtend(baseEntity), - NO_COMPONENT, - instanceArch.getEntity(instanceRow) - ) - } + onUpdated(true, moveTo, newRow) } /** @@ -357,45 +267,40 @@ class Archetype internal constructor( internal fun removeComponent( row: Int, component: ComponentId, - callEvent: Boolean, + onModify: (Archetype, row: Int, onComplete: (Archetype, Int) -> Unit) -> Unit = { a, r, onComplete -> + onComplete(a, r) + }, ): Boolean { val entityId = ids[row].toULong() if (component !in type) return false - if (callEvent) { - // Call event first, then ensure component is removed - callComponentModifyEvent(comps.onRemove, component, row) { finalArch, finalRow -> - if (component !in finalArch.type) return true // Case where listeners manually removed component - val moveTo = finalArch - component - moveTo.moveWithoutComponent(finalArch, finalRow, component, entityId) - finalArch.removeEntity(row) - } - return true + // We run onModify before the component is removed, since observers will be interested in the component data, + // Then take the result archetype after onModify and ensure the component is removed from there + onModify(this, row) { finalArch, finalRow -> + val moveTo = finalArch - component + moveTo.moveWithoutComponent(finalArch, finalRow, component, entityId) + finalArch.removeEntity(finalRow) } - - val moveTo = this - component - - moveTo.moveWithoutComponent(this, row, component, entityId) - removeEntity(row) return true } - private fun unregisterIfEmpty() { - if (allowUnregister == FALSE) return - if (ids.size == 0 && type.size != 0 && componentAddEdges.size == 0) { - if (allowUnregister == UNKNOWN) allowUnregister = - if (type.contains(comps.keepEmptyArchetype)) FALSE else TRUE - if (allowUnregister == FALSE) return - queryManager.unregisterArchetype(this) - unregistered = true - componentRemoveEdges.forEach { id, archetype -> - archetype.componentAddEdges.remove(id.toLong()) - archetype.unregisterIfEmpty() - } - componentRemoveEdges.clear() - } - } +// TODO reimplement +// private fun unregisterIfEmpty() { +// if (allowUnregister == FALSE) return +// if (ids.size == 0 && type.size != 0 && componentAddEdges.size == 0) { +// if (allowUnregister == UNKNOWN) allowUnregister = +// if (type.contains(comps.keepEmptyArchetype)) FALSE else TRUE +// if (allowUnregister == FALSE) return +// queryManager.unregisterArchetype(this) +// unregistered = true +// componentRemoveEdges.forEach { id, archetype -> +// archetype.componentAddEdges.remove(id.toLong()) +// archetype.unregisterIfEmpty() +// } +// componentRemoveEdges.clear() +// } +// } /** Gets all the components associated with an entity at a [row]. */ internal fun getComponents(row: Int, add: Pair? = null): Array { @@ -422,8 +327,8 @@ class Archetype internal constructor( * All other roles are ignored for the [target]. */ internal fun getRelations(kind: ComponentId, target: EntityId): List { - val specificKind = kind and ENTITY_MASK != comps.any - val specificTarget = target and ENTITY_MASK != comps.any + val specificKind = kind and ENTITY_MASK != ReservedComponents.ANY + val specificTarget = target and ENTITY_MASK != ReservedComponents.ANY return when { specificKind && specificTarget -> listOf(Relation.of(kind, target)) specificTarget -> getRelationsByTarget(target).map { Relation.of(it) } @@ -475,7 +380,7 @@ class Archetype internal constructor( if (index != -1) ids.removeAt(index) componentData.fastForEach { it.removeAt(lastIndex) } - unregisterIfEmpty() +// unregisterIfEmpty() TODO reimplement } override fun equals(other: Any?): Boolean { 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 814c9b1b3..f7ce300d1 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 @@ -22,8 +22,7 @@ open class ArchetypeEngine( private val pipeline: Pipeline, private val logger: Logger, override val tickDuration: Duration, - override val coroutineContext: CoroutineContext = - (CoroutineScope(Dispatchers.Default) + CoroutineName("Geary Engine")).coroutineContext + override val coroutineContext: CoroutineContext ) : TickingEngine() { /** Describes how to individually tick each system */ 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 ee594100b..3160a6c00 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 @@ -2,26 +2,22 @@ package com.mineinabyss.geary.engine.archetypes import co.touchlab.stately.concurrency.Synchronizable import co.touchlab.stately.concurrency.synchronize +import com.mineinabyss.geary.components.ReservedComponents import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.EntityIdArray import com.mineinabyss.geary.datatypes.family.Family import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap -import com.mineinabyss.geary.datatypes.toEntityArray -import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.QueryManager import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.fastForEach import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.geary.systems.query.Query -class ArchetypeQueryManager( - val components: ComponentProvider, -) : QueryManager { +class ArchetypeQueryManager : QueryManager { private val queries = mutableListOf>() private val archetypes = Family2ObjectArrayMap( - components = components.types, getIndex = { it.id }, setIndex = { it, index -> it.id = index } ) @@ -68,8 +64,8 @@ class ArchetypeQueryManager( } override fun childrenOf(parent: EntityId): EntityIdArray { - return getEntitiesMatching(family(components) { - hasRelation(this@ArchetypeQueryManager.components.types.childOf, parent) + return getEntitiesMatching(family { + hasRelation(ReservedComponents.CHILD_OF, parent) }) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt index e9d721bf6..605a782d1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/ComponentAsEntityProvider.kt @@ -1,40 +1,30 @@ package com.mineinabyss.geary.engine.archetypes import co.touchlab.kermit.Logger -import co.touchlab.stately.concurrency.Synchronizable -import co.touchlab.stately.concurrency.synchronize import com.mineinabyss.geary.components.ComponentInfo +import com.mineinabyss.geary.components.ReservedComponents import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.engine.ComponentProvider -import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityProvider -import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations import kotlin.reflect.KClassifier class ComponentAsEntityProvider( val entityProvider: EntityProvider, val logger: Logger, ) : ComponentProvider { - override val types: Components = Components(this) private val classToComponentMap = mutableMapOf() - private val classToComponentMapLock = Synchronizable() +// private val classToComponentMapLock = Synchronizable() TODO async support necessary? - internal fun createComponentInfo() { - logger.v("Registering ComponentInfo component") - //Register an entity for the ComponentInfo component, otherwise getComponentIdForClass does a StackOverflow - val componentInfo = entityProvider.create() - classToComponentMap[ComponentInfo::class] = componentInfo.toLong() - //TODO is this actually necessary? -// componentInfo.set(ComponentInfo(ComponentInfo::class), noEvent = true) + init { + createReservedComponents() } - override fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId = - classToComponentMapLock.synchronize { - val id = classToComponentMap.getOrElse(kClass) { - return@synchronize registerComponentIdForClass(kClass) - } - return@synchronize id.toULong() + override fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId { + val id = classToComponentMap.getOrElse(kClass) { + return registerComponentIdForClass(kClass) } + return id.toULong() + } private fun registerComponentIdForClass(kClass: KClassifier): ComponentId { logger.v("Registering new component: $kClass") @@ -43,4 +33,11 @@ class ComponentAsEntityProvider( classToComponentMap[kClass] = compEntity.toLong() return compEntity } + + private fun createReservedComponents() { + logger.v("Creating reserved components") + ReservedComponents.reservedComponents.forEach { (kClass, id) -> + classToComponentMap[kClass] = id.toLong() + } + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt index fcbf88145..3e688d50a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/EntityByArchetypeProvider.kt @@ -1,25 +1,27 @@ package com.mineinabyss.geary.engine.archetypes import co.touchlab.stately.concurrency.AtomicLong -import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.datatypes.EntityId +import com.mineinabyss.geary.datatypes.EntityStack +import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityProvider import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations -import com.mineinabyss.geary.helpers.* +import com.mineinabyss.geary.helpers.NO_COMPONENT +import com.mineinabyss.geary.components.ReservedComponents import com.mineinabyss.geary.observers.EventRunner -import com.mineinabyss.geary.observers.events.OnEntityRemoved class EntityRemove( - private val removedEntities: EntityStack, + private val entityProvider: EntityByArchetypeProvider, private val reader: ArchetypeReadOperations, private val write: ArchetypeMutateOperations, private val records: ArrayTypeMap, private val components: Components, private val eventRunner: EventRunner, private val queryManager: ArchetypeQueryManager, -){ +) { /** Removes an entity, freeing up its entity id for later reuse. */ fun remove(entity: EntityId) { if (!reader.has(entity, components.suppressRemoveEvent)) @@ -44,22 +46,24 @@ class EntityRemove( records.runOn(entity) { archetype, row -> archetype.removeEntity(row) records.remove(entity) - removedEntities.push(entity) + entityProvider.removedEntities.push(entity) } } } class EntityByArchetypeProvider( private val reuseIDsAfterRemoval: Boolean = true, + private val archetypeProvider: ArchetypeProvider, + private val records: ArrayTypeMap, ) : EntityProvider { - private lateinit var records: ArrayTypeMap - - // private val archetypeProvider: ArchetypeProvider by lazy { archetypes.archetypeProvider } - private lateinit var root: Archetype - internal val removedEntities: EntityStack = EntityStack() private val currId = AtomicLong(0L) + init { + // Allocate reserved components + repeat(ReservedComponents.reservedComponents.size) { create() } + } + override fun create(): EntityId { val entity: EntityId = if (reuseIDsAfterRemoval) { removedEntities.popOrElse { (currId.incrementAndGet() - 1).toULong() } @@ -69,13 +73,8 @@ class EntityByArchetypeProvider( return entity } - fun init(records: ArrayTypeMap, root: Archetype) { - this.records = records - this.root = root - } - private fun createRecord(entity: EntityId) { - val root = root + val root = archetypeProvider.rootArchetype val row = root.createWithoutData(entity) records[entity, root] = row } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt index 1e27b6243..044e0dd8c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/SimpleArchetypeProvider.kt @@ -7,15 +7,9 @@ import co.touchlab.stately.concurrency.synchronize import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -import com.mineinabyss.geary.engine.Components -import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations -import com.mineinabyss.geary.observers.EventRunner class SimpleArchetypeProvider( private val records: ArrayTypeMap, - private val write: ArchetypeMutateOperations, - private val eventRunner: EventRunner, - private val components: Components, private val queryManager: ArchetypeQueryManager, ) : ArchetypeProvider { override val rootArchetype: Archetype by lazy { @@ -39,12 +33,8 @@ class SimpleArchetypeProvider( return Archetype( type = type, id = id, - write = write, records = records, archetypeProvider = this, - eventRunner = eventRunner, - comps = components, - queryManager = queryManager ) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt index f6df3ec66..466199fd8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeMutateOperations.kt @@ -4,25 +4,20 @@ import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityMutateOperations +import com.mineinabyss.geary.engine.QueryManager +import com.mineinabyss.geary.engine.archetypes.Archetype import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider -import com.mineinabyss.geary.engine.archetypes.ArchetypeQueryManager -import com.mineinabyss.geary.engine.archetypes.SimpleArchetypeProvider +import com.mineinabyss.geary.helpers.NO_COMPONENT import com.mineinabyss.geary.observers.EventRunner +import com.mineinabyss.geary.observers.events.OnExtend class ArchetypeMutateOperations( + private val archetypeProvider: ArchetypeProvider, private val records: ArrayTypeMap, - eventRunner: EventRunner, - components: Components, - queryManager: ArchetypeQueryManager, + private val components: Components, + private val eventRunner: EventRunner, + private val queryManager: QueryManager, ) : EntityMutateOperations { - val archetypeProvider: ArchetypeProvider = SimpleArchetypeProvider( - records = records, - write = this, - eventRunner = eventRunner, - components = components, - queryManager = queryManager, - ) - override fun setComponentFor( entity: EntityId, componentId: ComponentId, @@ -33,7 +28,7 @@ class ArchetypeMutateOperations( // Only add HOLDS_DATA if this isn't a relation. All relations implicitly hold data currently and that bit // corresponds to the component part of the relation. val componentWithRole = componentId.withRole(HOLDS_DATA) - archetype.setComponent(row, componentWithRole, data, !noEvent) + setComponent(archetype, row, componentWithRole, data, !noEvent) } } @@ -43,24 +38,74 @@ class ArchetypeMutateOperations( noEvent: Boolean, ) { records.runOn(entity) { archetype, row -> - archetype.addComponent(row, componentId.withoutRole(HOLDS_DATA), !noEvent) + addComponent(archetype, row, componentId.withoutRole(HOLDS_DATA), !noEvent) } } override fun extendFor(entity: EntityId, base: EntityId) { records.runOn(base) { archetype, row -> records.runOn(entity) { entityArch, entityRow -> - archetype.instantiateTo(row, entityArch, entityRow) + instantiateTo(archetype, row, entityArch, entityRow) + } + } + } + + @Suppress("NAME_SHADOWING") // Want to make sure original arch/row is not accidentally accessed + private fun instantiateTo( + baseArchetype: Archetype, + baseRow: Int, + instanceArch: Archetype, + instanceRow: Int, + callEvent: Boolean = true, + ) { + val baseEntity = baseArchetype.getEntity(baseRow) + val instanceEntity = instanceArch.getEntity(instanceRow) + var instanceArch = instanceArch + var instanceRow = instanceRow + + addComponent(instanceArch, instanceRow, Relation.of(components.instanceOf, baseEntity).id, true) { arch, row -> + instanceArch = arch; instanceRow = row + } + + val noInheritComponents = baseArchetype.getRelationsByKind(components.noInherit).map { Relation.of(it).target } + baseArchetype.type.filter { !it.holdsData() && it !in noInheritComponents }.forEach { + addComponent(instanceArch, instanceRow, it, true) { arch, row -> instanceArch = arch; instanceRow = row } + } + baseArchetype.dataHoldingType.forEach { + if (it.withoutRole(HOLDS_DATA) in noInheritComponents) return@forEach + setComponent(instanceArch, instanceRow, it, baseArchetype.getUnsafe(baseRow, it), true) { arch, row -> + instanceArch = arch; instanceRow = row } } + + queryManager.childrenOf(baseEntity).forEach { child -> + // Add instanceEntity as parent + addComponentFor(instanceEntity, components.couldHaveChildren, true) + addComponentFor(child, Relation.of(components.childOf, instanceEntity).id, false) + } + records.runOn(instanceEntity) { arch, row -> instanceArch = arch; instanceRow = row } + + if (callEvent) eventRunner.callEvent( + components.onExtend, + OnExtend(baseEntity), + NO_COMPONENT, + instanceArch.getEntity(instanceRow) + ) } override fun removeComponentFor(entity: EntityId, componentId: ComponentId, noEvent: Boolean): Boolean { val a = records.runOn(entity) { archetype, row -> - archetype.removeComponent(row, componentId.withRole(HOLDS_DATA), !noEvent) + archetype.removeComponent(row, componentId.withRole(HOLDS_DATA), onModify = { moveTo, newRow, onComplete -> + if (!noEvent) callComponentModifyEvent(moveTo, components.onRemove, componentId, newRow, onComplete) + }) } val b = records.runOn(entity) { archetype, row -> - archetype.removeComponent(row, componentId.withoutRole(HOLDS_DATA), !noEvent) + archetype.removeComponent( + row, + componentId.withoutRole(HOLDS_DATA), + onModify = { moveTo, newRow, onComplete -> + if (!noEvent) callComponentModifyEvent(moveTo, components.onRemove, componentId, newRow, onComplete) + }) } return a || b // return whether anything was changed } @@ -76,4 +121,56 @@ class ArchetypeMutateOperations( records[entity, archetypeProvider.rootArchetype] = newRow } } + + private inline fun callComponentModifyEvent( + archetype: Archetype, + eventType: ComponentId, + involvedComp: ComponentId, + row: Int, + onComplete: (Archetype, row: Int) -> Unit = { _, _ -> }, + ) { + val entity = archetype.getEntity(row) + callComponentModifyEvent(archetype, eventType, involvedComp, row) + // Don't have any way to know final archetype and row without re-reading + records.runOn(entity, onComplete) + } + + private fun callComponentModifyEvent( + archetype: Archetype, + eventType: ComponentId, + involvedComp: ComponentId, + row: Int, + ) { + val entity = archetype.getEntity(row) + eventRunner.callEvent(eventType, null, involvedComp, entity) + } + + private inline fun addComponent( + archetype: Archetype, + row: Int, + componentId: ComponentId, + callEvent: Boolean, + onUpdated: (Archetype, row: Int) -> Unit = { _, _ -> }, + ) { + archetype.addComponent(row, componentId, onUpdated = { moveTo, newRow -> + if (callEvent) callComponentModifyEvent(moveTo, components.onAdd, componentId, newRow, onUpdated) + }) + } + + @Suppress("NAME_SHADOWING") // Want to make sure original arch/row is not accidentally accessed + private inline fun setComponent( + archetype: Archetype, + row: Int, + componentId: ComponentId, + data: Component, + callEvent: Boolean, + onUpdated: (Archetype, row: Int) -> Unit = { _, _ -> }, + ) { + archetype.setComponent(row, componentId, data, onUpdated = { firstSet, archetype, row -> + if (callEvent) callComponentModifyEvent(archetype, components.onSet, componentId, row) { archetype, row -> + if (firstSet) callComponentModifyEvent(archetype, components.onFirstSet, componentId, row, onUpdated) + else onUpdated(archetype, row) + } else onUpdated(archetype, row) + }) + } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt index 10e13e7d9..5cc91093f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/engine/archetypes/operations/ArchetypeReadOperations.kt @@ -2,11 +2,8 @@ package com.mineinabyss.geary.engine.archetypes.operations import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.Components import com.mineinabyss.geary.engine.EntityReadOperations -import com.mineinabyss.geary.engine.id -import com.mineinabyss.geary.helpers.toGeary import com.mineinabyss.geary.systems.accessors.RelationWithData class ArchetypeReadOperations( 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 deleted file mode 100644 index a713dfff4..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ArchetypeHelpers.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mineinabyss.geary.helpers - -import com.mineinabyss.geary.datatypes.EntityType -import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.ArchetypeEngineModule - -fun EntityType.getArchetype(world: ArchetypeEngineModule): Archetype = - world.write.archetypeProvider.getArchetype(this) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt index d0c6f9be2..a79ffc4be 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/ComponentHelpers.kt @@ -13,7 +13,7 @@ fun EntityId.readableString(world: Geary): String = buildString { if (id.hasRole(RELATION)) append("R") else append('-') if (id.hasRole(HOLDS_DATA)) append("D") else append('-') append(" ") - val componentName = (id.getComponentInfo(world)?.kClass as? KClass<*>)?.simpleName + val componentName = (world.getComponentInfo(id)?.kClass as? KClass<*>)?.simpleName if (componentName == null) append(id and ENTITY_MASK) else append(componentName) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt index 704c4269a..148ec40dd 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EngineHelpers.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KType import kotlin.reflect.typeOf /** Creates a new empty entity. May reuse recently deleted entity ids. */ -fun Geary.entity(): Entity = Entity(module.entityProvider.create(), this) +fun Geary.entity(): Entity = Entity(entityProvider.create(), this) /** @see entity */ inline fun Geary.entity(run: Entity.() -> Unit): Entity = entity().apply(run) @@ -19,7 +19,7 @@ inline fun Geary.temporaryEntity( run: (Entity) -> T, ): T { val entity = entity { - add(module.components.suppressRemoveEvent, noEvent = true) + add(components.suppressRemoveEvent, noEvent = true) } return try { run(entity) @@ -30,7 +30,7 @@ inline fun Geary.temporaryEntity( inline fun Geary.component(): Entity = component(T::class) -fun Geary.component(kClass: KClass<*>): Entity = componentId(kClass).toGeary(this) +fun Geary.component(kClass: KClass<*>): Entity = componentId(kClass).toGeary() /** Gets or registers the id of a component of type [T] */ inline fun Geary.componentId(): ComponentId = componentId(T::class) @@ -45,7 +45,7 @@ fun Geary.componentId(kType: KType): ComponentId = /** Gets or registers the id of a component by its [kClass]. */ fun Geary.componentId(kClass: KClassifier): ComponentId = - module.componentProvider.getOrRegisterComponentIdForClass(kClass) + componentProvider.getOrRegisterComponentIdForClass(kClass) @Deprecated("Should not be getting an id for an id!", ReplaceWith("componentId(component)")) @@ -54,6 +54,6 @@ fun componentId(kClass: KClass): Nothing = error("Trying to access id for component id") /** Gets the [ComponentInfo] component from a component's id. */ -fun ComponentId.getComponentInfo(world: Geary): ComponentInfo? = this.toGeary(world).get() +fun Geary.getComponentInfo(component: ComponentId): ComponentInfo? = component.toGeary().get() inline fun Geary.cId(): ComponentId = componentId() diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt index 4dfd3378f..19c01731f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/EntityHelpers.kt @@ -6,15 +6,4 @@ import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.modules.Geary -/** Gets the entity associated with this [EntityId], stripping it of any roles, and runs code on it. */ -inline fun EntityId.toGeary(world: Geary, run: Entity.() -> Unit): Entity = toGeary(world).apply(run) - -/** Gets the entity associated with this [EntityId], stripping it of any roles. */ -fun EntityId.toGeary(world: Geary): Entity = Entity(this and ENTITY_MASK, world) - -/** Gets the entity associated with this [Long]. */ -fun Long.toGeary(world: Geary): Entity = Entity(toULong() and ENTITY_MASK, world) - -val Geary.NO_ENTITY: Entity get() = 0L.toGeary(this) - const val NO_COMPONENT: ComponentId = 0uL diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt index e535c5f59..97a15ca9f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/helpers/Relationship.kt @@ -47,16 +47,16 @@ fun Entity.clearChildren() { /** Gets the first parent of this entity */ val Entity.parent: Entity? - get() = getRelations().firstOrNull()?.target?.toGeary(world) + get() = with(world) { getRelations().firstOrNull()?.target?.toGeary() } /** Runs code on the first parent of this entity. */ inline fun Entity.onParent( parent: Entity? = this.parent, - run: Entity.() -> Unit + run: Entity.() -> Unit, ) { parent ?: return run(parent) } val Entity.parents: Set - get() = getRelations().mapTo(mutableSetOf()) { it.target.toGeary(world) } + get() = with(world) { getRelations().mapTo(mutableSetOf()) { it.target.toGeary() } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineInitializer.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineInitializer.kt new file mode 100644 index 000000000..7480af979 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineInitializer.kt @@ -0,0 +1,19 @@ +package com.mineinabyss.geary.modules + +import com.mineinabyss.geary.engine.Pipeline +import com.mineinabyss.geary.engine.archetypes.ArchetypeEngine +import com.mineinabyss.geary.engine.archetypes.ComponentAsEntityProvider + +class ArchetypeEngineInitializer( + val beginTickingOnStart: Boolean, + private val pipeline: Pipeline, + private val engine: ArchetypeEngine, +) : EngineInitializer { + override fun init() { + } + + override fun start() { + pipeline.runStartupTasks() + if (beginTickingOnStart) engine.start() + } +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt index f462437c4..84c47af3c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt @@ -2,67 +2,92 @@ package com.mineinabyss.geary.modules import co.touchlab.kermit.Logger import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -import com.mineinabyss.geary.engine.Components -import com.mineinabyss.geary.engine.PipelineImpl +import com.mineinabyss.geary.datatypes.maps.SynchronizedArrayTypeMap +import com.mineinabyss.geary.datatypes.maps.TypeMap +import com.mineinabyss.geary.engine.* import com.mineinabyss.geary.engine.archetypes.* import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeMutateOperations import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations import com.mineinabyss.geary.observers.ArchetypeEventRunner -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.observers.EventRunner +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.plus +import org.koin.core.module.dsl.bind +import org.koin.core.module.dsl.singleOf +import org.koin.core.module.dsl.withOptions +import org.koin.dsl.module +import kotlin.coroutines.CoroutineContext import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -class ArchetypeEngineModule( - tickDuration: Duration = 50.milliseconds, -) : GearyModule { - val records: ArrayTypeMap = ArrayTypeMap() - override val logger = Logger.withTag("Geary") - override val entityProvider = EntityByArchetypeProvider() - override val defaults: Defaults = Defaults() +internal object ArchetypesModules { + // Module for any classes without other dependencies as parameters + val noDependencies get() = module { + single { Geary } + single { if (getProperty("useSynchronized")) SynchronizedArrayTypeMap() else ArrayTypeMap() } withOptions { + bind() + } + } - override val componentProvider = ComponentAsEntityProvider(entityProvider, logger) - override val components = Components(componentProvider) - override val queryManager = ArchetypeQueryManager(componentProvider) - override val read = ArchetypeReadOperations(components, records) - override val pipeline = PipelineImpl(queryManager) - override val engine = ArchetypeEngine(pipeline, logger, tickDuration) - override val eventRunner = ArchetypeEventRunner(read, components.observer, componentProvider, records) - override val write = ArchetypeMutateOperations( - records, - eventRunner = eventRunner, - components = components, - queryManager = queryManager, - ) - override val entityRemoveProvider = EntityRemove( - removedEntities = entityProvider.removedEntities, - reader = read, - write = write, - records = records, - components = components, - eventRunner = eventRunner, - queryManager = queryManager, - ) + val archetypes get() = module { + includes(noDependencies) + single { ArchetypeQueryManager() } withOptions { bind() } + singleOf(::SimpleArchetypeProvider) { bind() } + } - companion object : GearyModuleProviderWithDefault { - override fun default(): ArchetypeEngineModule { - return ArchetypeEngineModule() - } + val entities get() = module { + includes(archetypes) + single { + EntityByArchetypeProvider(getProperty("reuseIDsAfterRemoval"), get(), get()) + } withOptions { bind() } + } - override fun init(module: ArchetypeEngineModule) { - DI.add(module) - DI.add(module) - module.entityProvider.init(module.records, module.write.archetypeProvider.rootArchetype) - module.componentProvider.createComponentInfo() - } + val components get() = module { + includes(entities) + singleOf(::ComponentAsEntityProvider) { bind() } + singleOf(::Components) + } - override fun start(module: ArchetypeEngineModule) { - TODO("Fix") -// module { -// on(GearyPhase.ENABLE) { -// module.engine.start() -// } -// } -// geary.pipeline.runStartupTasks() - } + val core get() = module { + includes(components) + singleOf(::ArchetypeReadOperations) { bind() } + singleOf(::PipelineImpl) { bind() } + } + + val engine get() = module { + includes(core) + single { + ArchetypeEngine(get(), get(), getProperty("tickDuration"), getProperty("engineThread")) + } withOptions { bind() } } } + +fun ArchetypeEngineModule( + tickDuration: Duration = 50.milliseconds, + reuseIDsAfterRemoval: Boolean = true, + useSynchronized: Boolean = false, + beginTickingOnStart: Boolean = true, + defaults: Defaults = Defaults(), + engineThread: CoroutineContext = (CoroutineScope(Dispatchers.Default) + CoroutineName("Geary Engine")).coroutineContext, +) = GearyModule( + module { + includes(ArchetypesModules.engine) + singleOf(::ArchetypeEventRunner) { bind() } + singleOf(::ArchetypeMutateOperations) { bind() } + singleOf(::EntityRemove) + single { + ArchetypeEngineInitializer(getProperty("beginTickingOnStart"), get(), get()) + } withOptions { + bind() + } + }, properties = mapOf( + "tickDuration" to tickDuration, + "reuseIDsAfterRemoval" to reuseIDsAfterRemoval, + "useSynchronized" to useSynchronized, + "beginTickingOnStart" to beginTickingOnStart, + "defaults" to defaults, + "engineThread" to engineThread + ) +) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/EngineInitializer.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/EngineInitializer.kt new file mode 100644 index 000000000..26fd2cf48 --- /dev/null +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/EngineInitializer.kt @@ -0,0 +1,6 @@ +package com.mineinabyss.geary.modules + +interface EngineInitializer { + fun init() + fun start() +} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index 67d75a4d7..1067d8687 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -4,14 +4,28 @@ import co.touchlab.kermit.Logger import co.touchlab.kermit.mutableLoggerConfigInit import co.touchlab.kermit.platformLogWriter import com.mineinabyss.geary.addons.dsl.Addon +import com.mineinabyss.geary.datatypes.* +import com.mineinabyss.geary.datatypes.family.Family +import com.mineinabyss.geary.datatypes.family.MutableFamily +import com.mineinabyss.geary.datatypes.family.family +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.engine.* +import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider +import com.mineinabyss.geary.engine.archetypes.EntityRemove import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.helpers.componentIdWithNullable +import com.mineinabyss.geary.observers.EventRunner import com.mineinabyss.geary.observers.builders.ObserverWithData import com.mineinabyss.geary.observers.builders.ObserverWithoutData import com.mineinabyss.geary.systems.builders.SystemBuilder import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.geary.systems.query.Query -import com.mineinabyss.idofront.di.DIContext -import kotlin.jvm.JvmName +import org.koin.core.Koin +import org.koin.core.KoinApplication +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import kotlin.reflect.KClass /** * Root class for users to access geary functionality. @@ -23,42 +37,40 @@ import kotlin.jvm.JvmName * Any functions that modify the state of the engine modify it right away, * they are not scheduled for load phases like [GearySetup] is. */ -open class Geary( - val module: GearyModule, - val context: DIContext, - val logger: Logger = module.logger, -) { - @JvmName("getAddon1") +interface Geary : KoinComponent { + abstract val application: KoinApplication + open val logger: Logger get() = application.koin.get() + override fun getKoin(): Koin = application.koin + // By default, we always get the latest instance of deps, the Impl class gets them once for user + // access where the engine isn't expected to be reloaded (ex. like it might be in tests) + val eventRunner: EventRunner get() = get() + val read: EntityReadOperations get() = get() + val write: EntityMutateOperations get() = get() + val queryManager: QueryManager get() = get() + val pipeline: Pipeline get() = get() + val entityProvider: EntityProvider get() = get() + val entityRemoveProvider: EntityRemove get() = get() + val components: Components get() = get() + val componentProvider: ComponentProvider get() = get() + val records: ArrayTypeMap get() = get() + val engine: GearyEngine get() = get() + + // @JvmName("getAddon1") fun , Inst> getAddon(addon: T): Inst = TODO() - @JvmName("getAddon2") - fun , Inst> getAddon(addon: T?): Inst? = TODO() + + // @JvmName("getAddon2") +// fun , Inst> getAddon(addon: T?): Inst? = TODO() // Queries - fun cache( - query: T, - ): CachedQuery { - return module.queryManager.trackQuery(query) + fun findEntities(family: Family): EntityArray { + return queryManager.getEntitiesMatching(family).toEntityArray(world = this) } - // TODO simple api for running queries in place without caching - inline fun execute( + fun cache( query: T, - run: T.() -> Unit = {}, ): CachedQuery { - TODO() - } - - inline fun observe(): ObserverWithoutData { - return ObserverWithoutData(listOf(componentId()), world = this) { - module.eventRunner.addObserver(it) - } - } - - inline fun observeWithData(): ObserverWithData { - return ObserverWithData(listOf(componentId()), world = this) { - module.eventRunner.addObserver(it) - } + return queryManager.trackQuery(query) } fun system( @@ -74,8 +86,76 @@ open class Geary( .substringAfter("Kt.") .substringAfter("create") - return SystemBuilder(defaultName, query, module.pipeline) + return SystemBuilder(defaultName, query, pipeline) + } + + + fun relationOf(kind: KClass<*>, target: KClass<*>): Relation = + Relation.of(componentId(kind), componentId(target)) + + fun EntityType.getArchetype(): Archetype = + get().getArchetype(this) + + /** Gets the entity associated with this [EntityId], stripping it of any roles. */ + fun EntityId.toGeary(): Entity = Entity(this and ENTITY_MASK, this@Geary) + + /** Gets the entity associated with this [Long]. */ + fun Long.toGeary(): Entity = Entity(toULong() and ENTITY_MASK, this@Geary) + + val NO_ENTITY: Entity get() = 0L.toGeary() + + companion object : Logger(mutableLoggerConfigInit(listOf(platformLogWriter())), "Geary") { + operator fun invoke(application: KoinApplication, logger: Logger? = null): Geary = Impl(application, logger) + } + + class Impl( + override val application: KoinApplication, + logger: Logger? = null, + ) : Geary { + override val logger: Logger = logger ?: super.logger + override val eventRunner: EventRunner by inject() + override val read: EntityReadOperations by inject() + override val write: EntityMutateOperations by inject() + override val queryManager: QueryManager by inject() + override val pipeline: Pipeline by inject() + override val entityProvider: EntityProvider by inject() + override val entityRemoveProvider: EntityRemove by inject() + override val components: Components by inject() + override val componentProvider: ComponentProvider by inject() + override val records: ArrayTypeMap by inject() + override val engine: GearyEngine by inject() + } + + fun stringify() = application.toString().removePrefix("org.koin.core.KoinApplication") +} + +inline fun Geary.relationOf(): Relation = + Relation.of(componentIdWithNullable(), componentId()) + +inline fun Geary.relationOf(target: Entity): Relation = + Relation.of(componentIdWithNullable(), target.id) + +inline fun Geary.get() = application.koin.get() + +// TODO simple api for running queries in place without caching +inline fun execute( + query: T, + run: T.() -> Unit = {}, +): CachedQuery { + TODO() +} + +inline fun Geary.observe(): ObserverWithoutData { + return ObserverWithoutData(listOf(componentId()), world = this) { + eventRunner.addObserver(it) } +} - companion object : Logger(mutableLoggerConfigInit(listOf(platformLogWriter())), "Geary") +inline fun Geary.observeWithData(): ObserverWithData { + return ObserverWithData(listOf(componentId()), world = this) { + eventRunner.addObserver(it) + } } + +inline fun Geary.findEntities(init: MutableFamily.Selector.And.() -> Unit)= + findEntities(family(init)) 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 deleted file mode 100644 index 0a3cd3857..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyConfiguration.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.mineinabyss.geary.modules - -import com.mineinabyss.geary.addons.GearyPhase -import com.mineinabyss.geary.addons.Namespaced -import com.mineinabyss.geary.addons.dsl.GearyAddon -import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.addons.dsl.GearyDSL -import com.mineinabyss.idofront.di.DI -import kotlin.reflect.KClass - -//@GearyDSL -//class GearyConfiguration( -// val module: GearyModule -//) { -// inline fun , reified Module : Any> install( -// addon: T, -// ): Module { -// val module = DI.getOrNull() -// if (module != null) return module -// return install(addon, addon.default()) -// } -// -// inline fun , reified Module : Any> install( -// addon: T, -// module: Module, -// ): Module = with(addon) { -// DI.add(module) -// module.install() -// }.let { module } -// -// fun namespace(namespace: String, configure: Namespaced.() -> Unit) { -// Namespaced(namespace, this).configure() -// } -// -// /** Runs a block during [GearyPhase.INIT_COMPONENTS] */ -// fun components(configure: Geary.() -> Unit) { -// on(GearyPhase.INIT_COMPONENTS) { -// module.configure() -// } -// } -// -// /** Runs a block during [GearyPhase.INIT_SYSTEMS] */ -// fun systems(configure: Geary.() -> Unit) { -// on(GearyPhase.INIT_SYSTEMS) { -// module.configure() -// } -// } -// -// /** Runs a block during [GearyPhase.INIT_ENTITIES] */ -// fun entities(configure: Geary.() -> Unit) { -// on(GearyPhase.INIT_ENTITIES) { -// module.configure() -// } -// } -// -// /** -// * Allows defining actions that should run at a specific phase during startup -// * -// * Within its context, invoke a [GearyPhase] to run something during it, ex: -// * -// * ``` -// * GearyLoadPhase.ENABLE { -// * // run code here -// * } -// * ``` -// */ -// fun on(phase: GearyPhase, run: () -> Unit) { -// geary.pipeline.runOnOrAfter(phase, run) -// } -//} 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 a91b7137a..287816eb8 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 @@ -1,70 +1,40 @@ package com.mineinabyss.geary.modules -import co.touchlab.kermit.Logger -import com.mineinabyss.geary.addons.dsl.* -import com.mineinabyss.geary.engine.* -import com.mineinabyss.geary.engine.archetypes.EntityRemove -import com.mineinabyss.geary.observers.EventRunner -import com.mineinabyss.idofront.di.DI -import com.mineinabyss.idofront.di.DIContext +import org.koin.core.module.Module +import org.koin.dsl.koinApplication +import org.koin.dsl.module -//val geary: GearyAPI by DI.observe() - -fun geary( - moduleProvider: GearyModuleProviderWithDefault, - context: DIContext = DI, - configure: GearySetup.() -> Unit = {}, -): UninitializedGearyModule { - val module = moduleProvider.default() - return geary(moduleProvider, module, context, configure) -} - -fun geary( - moduleProvider: GearyModuleProvider, - module: T, - context: DIContext = DI, +fun geary( + module: GearyModule, configure: GearySetup.() -> Unit = {}, ): UninitializedGearyModule { - moduleProvider.init(module) - val setup = GearySetup(module, context) + val application = koinApplication { + properties(module.properties) + modules(module.module) + } + val initializer = application.koin.get() + initializer.init() + val setup = GearySetup(application) configure(setup) - return UninitializedGearyModule(setup, moduleProvider as GearyModuleProvider) + return UninitializedGearyModule(setup, initializer) } data class UninitializedGearyModule( val setup: GearySetup, - val provider: GearyModuleProvider + val initializer: EngineInitializer, ) { inline fun configure(configure: GearySetup.() -> Unit) = setup.configure() - fun start(): Geary{ + fun start(): Geary { setup.addons.initAll(setup) - provider.start(setup.module) - setup.module.pipeline.runStartupTasks() // TODO keep pipeline separate, it shouldnt be used after init - return Geary(setup.module, setup.context) + initializer.start() + val world = Geary(setup.application) + world.pipeline.runStartupTasks() // TODO keep pipeline separate, it shouldnt be used after init + return world } } -/** - * Describes all the dependencies needed to initialize and run a Geary engine. - */ -@GearyDSL -interface GearyModule { - val logger: Logger - val entityProvider: EntityProvider - val entityRemoveProvider: EntityRemove - val componentProvider: ComponentProvider - - val read: EntityReadOperations - val write: EntityMutateOperations - - val queryManager: QueryManager - val components: Components - val engine: Engine - - val eventRunner: EventRunner - val pipeline: Pipeline - - val defaults: Defaults -} - +data class GearyModule( + val module: Module, + val properties: Map = emptyMap(), +) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProvider.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProvider.kt deleted file mode 100644 index 323240e8c..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.mineinabyss.geary.modules - -interface GearyModuleProvider { - fun init(module: T) - fun start(module: T) -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProviderWithDefault.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProviderWithDefault.kt deleted file mode 100644 index a093191f7..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearyModuleProviderWithDefault.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.mineinabyss.geary.modules - -interface GearyModuleProviderWithDefault : GearyModuleProvider { - fun default(): T -} diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt index a0b9e3938..fea42dfea 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt @@ -1,22 +1,22 @@ package com.mineinabyss.geary.modules +import co.touchlab.kermit.Logger import com.mineinabyss.geary.addons.Namespaced import com.mineinabyss.geary.addons.dsl.Addon -import com.mineinabyss.idofront.di.DIContext +import org.koin.core.KoinApplication /** * Represents a Geary engine whose dependencies have been created in a [GearyModule] and is ready to have addons * installed. Load phases are accessible here and will be called once start gets called. */ class GearySetup( - val module: GearyModule, - val context: DIContext, + val application: KoinApplication, ) { val addons = MutableAddons() - val logger = module.logger - val geary = Geary(module, context, logger) + val logger = application.koin.get() + val geary = Geary(application) - inline fun , Conf> install(addon: T, configure: Conf.() -> Unit = {}): T { + inline fun , Conf> install(addon: T, configure: Conf.() -> Unit = {}): T { addons.getOrPut(geary, addon).apply { config.configure() } return addon } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt index 96e66cd15..dc1607bc8 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt @@ -16,7 +16,7 @@ class MutableAddons { fun initAll(setup: GearySetup) { addons.forEach { (name, addon) -> - val geary = Geary(setup.module, setup.context, setup.module.logger.withTag(name)) + val geary = Geary(setup.application, setup.logger.withTag(name)) (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt index 3a1d34e59..03a74c7dc 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/TestEngineModule.kt @@ -1,35 +1,12 @@ package com.mineinabyss.geary.modules -import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap -import com.mineinabyss.geary.datatypes.maps.SynchronizedArrayTypeMap -import com.mineinabyss.geary.engine.archetypes.EntityByArchetypeProvider - /** * An engine module that initializes the engine but does not start it, useful for testing. * * No pipeline tasks are run, and the engine won't be scheduled for ticking. * Engine ticks may still be called manually. */ -//TODO swap to koin -class TestEngineModule( - reuseIDsAfterRemoval: Boolean = true, - useSynchronized: Boolean = false, -) : GearyModule by ArchetypeEngineModule( -) { - //TODO -// records = if (useSynchronized) SynchronizedArrayTypeMap() else ArrayTypeMap() - override val entityProvider = EntityByArchetypeProvider(reuseIDsAfterRemoval) - - companion object : GearyModuleProviderWithDefault { - override fun init(module: TestEngineModule) { - TODO() -// ArchetypeEngineModule.init(module) - } - - override fun start(module: TestEngineModule) = Unit - - override fun default(): TestEngineModule { - return TestEngineModule() - } - } -} +// TODO set beginTickingOnStart = false property +val TestEngineModule get() = ArchetypeEngineModule( + beginTickingOnStart = false +) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt index 0d96af1e1..64efb8e26 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/ArchetypeEventRunner.kt @@ -12,10 +12,11 @@ import com.mineinabyss.geary.helpers.fastForEach class ArchetypeEventRunner( val reader: ArchetypeReadOperations, - val observerComponent: ComponentId, val compProvider: ComponentProvider, val records: ArrayTypeMap ) : EventRunner { + val observerComponent: ComponentId = compProvider.id() + private val eventToObserversMap = EventToObserversMap(records) override fun addObserver(observer: Observer) { eventToObserversMap.addObserver(observer) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt index ff65bd83f..e29906497 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverBuilder.kt @@ -4,7 +4,6 @@ import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.GearyEntityType import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.ComponentProvider -import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.systems.query.Query import com.mineinabyss.geary.systems.query.ShorthandQuery @@ -56,7 +55,7 @@ data class ObserverBuilder( override fun exec(handle: Context.() -> Unit): Observer { val observer = Observer( matchQueries, - family(comp) { matchQueries.forEach { add(it.buildFamily()) } }, + family { matchQueries.forEach { add(it.buildFamily()) } }, involvedComponents, GearyEntityType(events.listenToEvents), events.mustHoldData, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt index d0116fd4a..34d99a29f 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/builders/ObserverEventsBuilder.kt @@ -3,7 +3,6 @@ package com.mineinabyss.geary.observers.builders import com.mineinabyss.geary.datatypes.* import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.id -import com.mineinabyss.geary.helpers.NO_ENTITY import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.systems.query.Query @@ -54,7 +53,7 @@ abstract class ObserverEventsBuilder : ExecutableObserver { abstract val mustHoldData: Boolean abstract val onBuild: (Observer) -> Unit - val comp: ComponentProvider get() = world.module.componentProvider + val comp: ComponentProvider get() = world.componentProvider abstract fun provideContext(entity: EntityId, data: Any?): Context diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt index e06ba94bd..fb991de9b 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/entity/EntityObserver.kt @@ -21,7 +21,7 @@ inline fun GearyEntity.observeWithData(): ObserverEventsBuilde fun GearyEntity.attachObserver(observer: Observer) { val observerEntity = world.entity { // TODO avoid cast - set(EventToObserversMap((world.module as ArchetypeEngineModule).records).apply { addObserver(observer) }) + set(EventToObserversMap(world.records).apply { addObserver(observer) }) addRelation(this@attachObserver) // Remove entity when original is removed } //TODO remove when prefabs auto propagate component adds down diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt index d358a223a..ce3d2a8f1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/observers/queries/CacheQueryAsMap.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.observers.queries import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.builders.ObserverContext import com.mineinabyss.geary.observers.events.OnFirstSet import com.mineinabyss.geary.observers.events.OnRemove 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 2163f00bc..c3954b0a1 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 @@ -2,18 +2,15 @@ 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.MutableFamily import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.withRole import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.componentIdWithNullable -import com.mineinabyss.geary.modules.ArchetypeEngineModule import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.relationOf import com.mineinabyss.geary.systems.accessors.type.* import com.mineinabyss.geary.systems.query.QueriedEntity -import com.mineinabyss.idofront.di.DI -import com.mineinabyss.idofront.di.DIContext import kotlin.reflect.typeOf abstract class AccessorOperations { @@ -29,7 +26,7 @@ abstract class AccessorOperations { /** Accesses a component, ensuring it is on the entity. */ protected inline fun QueriedEntity.get(): ComponentAccessor { return addAccessor { - ComponentAccessor(world.module.componentProvider, null, world.componentId().withRole(HOLDS_DATA)) + ComponentAccessor(world.componentProvider, null, world.componentId().withRole(HOLDS_DATA)) } } @@ -37,7 +34,7 @@ abstract class AccessorOperations { val t = typeOf() return addAccessor { val id = world.componentId(t).withRole(HOLDS_DATA) - val compAccessor = ComponentAccessor(world.module.componentProvider, null, id) + val compAccessor = ComponentAccessor(world.componentProvider, null, id) if (t.isMarkedNullable) ComponentOrDefaultAccessor(compAccessor, id) { null } else compAccessor @@ -46,7 +43,7 @@ abstract 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 { - return addAccessor { ComponentAccessor(world.module.componentProvider, null, Relation.of(world).id) } + return addAccessor { ComponentAccessor(world.componentProvider, null, world.relationOf().id) } } inline fun addAccessor(create: () -> T): T { @@ -95,14 +92,14 @@ abstract class AccessorOperations { * - Note: nullability rules are still upheld with [Any]. */ protected inline fun QueriedEntity.getRelations(): RelationsAccessor { - return addAccessor { RelationsAccessor(world.module.componentProvider, null, world.componentIdWithNullable(), world.componentIdWithNullable()) } + return addAccessor { RelationsAccessor(world.componentProvider, null, world.componentIdWithNullable(), world.componentIdWithNullable()) } } /** @see getRelations */ protected inline fun QueriedEntity.getRelationsWithData(): RelationsWithDataAccessor { return addAccessor { RelationsWithDataAccessor( - world.module.componentProvider, + world.componentProvider, null, world.componentIdWithNullable(), world.componentIdWithNullable() @@ -111,7 +108,7 @@ abstract class AccessorOperations { } protected operator fun QueriedEntity.invoke(init: MutableFamily.Selector.And.() -> Unit) { - val family = world.family(init) + val family = family(init) extraFamilies.add(family) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt index 903dfe9a9..4c3f9caba 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/accessors/RelationWithData.kt @@ -1,10 +1,8 @@ package com.mineinabyss.geary.systems.accessors import com.mineinabyss.geary.datatypes.Component -import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.EntityId import com.mineinabyss.geary.datatypes.Relation -import com.mineinabyss.geary.helpers.toGeary /** * Helper class for getting a compact overview of data stored in a relation. 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 f6d5febe7..81f1dacff 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 @@ -7,12 +7,10 @@ import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.Geary 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.Query -import kotlin.reflect.KProperty @OptIn(UnsafeAccessors::class) class ComponentAccessor( @@ -20,7 +18,7 @@ class ComponentAccessor( override val originalAccessor: Accessor?, val id: ComponentId ) : ReadWriteAccessor, FamilyMatching { - override val family = family(comp) { hasSet(id) } + override val family = family { hasSet(id) } private var cachedIndex = -1 private var cachedDataArray: MutableObjectList = mutableObjectListOf() 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 f8abc4372..9fd63f2e0 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,19 +7,18 @@ import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor import com.mineinabyss.geary.systems.query.Query class RelationsAccessor( - comp: ComponentProvider, + val comp: ComponentProvider, override val originalAccessor: Accessor?, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>, FamilyMatching { - override val family = family(comp) { hasRelation(kind, target) } + override val family = family { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null 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 1e98d1ea2..babba7d06 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 @@ -7,7 +7,6 @@ import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.engine.ComponentProvider import com.mineinabyss.geary.engine.archetypes.Archetype -import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.FamilyMatching import com.mineinabyss.geary.systems.accessors.ReadOnlyAccessor @@ -16,12 +15,12 @@ import com.mineinabyss.geary.systems.query.Query @OptIn(UnsafeAccessors::class) class RelationsWithDataAccessor( - comp: ComponentProvider, + val comp: ComponentProvider, override val originalAccessor: Accessor?, val kind: ComponentId, val target: EntityId, ) : ReadOnlyAccessor>>, FamilyMatching { - override val family = family(comp) { hasRelation(kind, target) } + override val family = family { hasRelation(kind, target) } private var cachedRelations = emptyList() private var cachedArchetype: Archetype? = null 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 deleted file mode 100644 index b31157e8c..000000000 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/builders/GlobalFunctions.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mineinabyss.geary.systems.builders - -import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.modules.GearyModule -import com.mineinabyss.geary.observers.builders.ObserverWithData -import com.mineinabyss.geary.observers.builders.ObserverWithoutData -import com.mineinabyss.geary.systems.query.CachedQuery -import com.mineinabyss.geary.systems.query.Query diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt index adb4f5c75..6175673b6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt @@ -5,7 +5,9 @@ import com.mineinabyss.geary.annotations.optin.UnsafeAccessors import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider import com.mineinabyss.geary.helpers.fastForEach +import com.mineinabyss.geary.modules.get class CachedQuery internal constructor(val query: T) { val matchedArchetypes: MutableList = mutableListOf() @@ -68,7 +70,7 @@ class CachedQuery internal constructor(val query: T) { val accessors = cachingAccessors // current archetype - var archetype = query.module.write.archetypeProvider.rootArchetype // avoid nullable perf loss + var archetype = query.world.get().rootArchetype // avoid nullable perf loss var upTo = 0 // current entity 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 e0ccb7a35..2a69ff9e3 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 @@ -6,8 +6,9 @@ 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.ArchetypeEngineModule +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.get import com.mineinabyss.geary.systems.accessors.Accessor import com.mineinabyss.geary.systems.accessors.AccessorOperations import com.mineinabyss.geary.systems.accessors.FamilyMatching @@ -16,18 +17,15 @@ open class QueriedEntity( final override val world: Geary, final override val cacheAccessors: Boolean, ) : AccessorOperations() { - val module = - world.module as? ArchetypeEngineModule ?: error("QueriedEntity can only be used with the ArchetypeEngineModule") - @PublishedApi @UnsafeAccessors - internal var archetype = (world.module as ArchetypeEngineModule).write.archetypeProvider.rootArchetype + internal var archetype = world.get().rootArchetype internal val extraFamilies: MutableList = mutableListOf() internal val props: MutableMap = mutableMapOf() - fun buildFamily(): Family.Selector.And = world.family { + fun buildFamily(): Family.Selector.And = family { accessors .filterIsInstance() .mapNotNull { it.family } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt index 9c8ca59de..0a69a7550 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/QueryShorthands.kt @@ -67,7 +67,7 @@ abstract class ShorthandQuery5(world: Geary) : ShorthandQuery(wor fun Geary.query() = object : Query(this) {} fun Geary.query(match: MutableFamily.Selector.And.() -> Unit) = object : Query(this) { - override fun ensure() = this { add(world.family(match)) } + override fun ensure() = this { add(family(match)) } } inline fun Geary.query( diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/components/ComponentAsEntityProviderTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/components/ComponentAsEntityProviderTest.kt new file mode 100644 index 000000000..2a4925660 --- /dev/null +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/components/ComponentAsEntityProviderTest.kt @@ -0,0 +1,15 @@ +package com.mineinabyss.geary.components + +import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.helpers.entity +import com.mineinabyss.geary.test.GearyTest +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class ComponentAsEntityProviderTest: GearyTest() { + @Test + fun `should correctly register reserved components`() { + entity() + componentId() shouldBe ReservedComponents.ANY + } +} diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/EntityTypeTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/EntityTypeTest.kt index 1901d2f9f..fb6d0cfe7 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/EntityTypeTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/EntityTypeTest.kt @@ -1,6 +1,6 @@ package com.mineinabyss.geary.datatypes -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt index 04161d465..0877f213d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/Family2ObjectArrayMapTest.kt @@ -3,44 +3,33 @@ package com.mineinabyss.geary.datatypes import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.datatypes.maps.Family2ObjectArrayMap import com.mineinabyss.geary.engine.ComponentProvider -import com.mineinabyss.geary.engine.Components import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactly import org.junit.jupiter.api.Test import kotlin.reflect.KClassifier -class MockComponentProvider : ComponentProvider{ - override val types: Components = Components(this) - - override fun getOrRegisterComponentIdForClass(kClass: KClassifier): ComponentId { - return 0uL //TODO increment - } -} - class Family2ObjectArrayMapTest { - val comp = MockComponentProvider() - @Test fun addAndRemoveOne() { - val familyMap = Family2ObjectArrayMap(comp.types) + val familyMap = Family2ObjectArrayMap() familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) - familyMap.match(family(comp) { has(1uL) }) shouldContainExactly listOf("a") + familyMap.match(family { has(1uL) }) shouldContainExactly listOf("a") familyMap.remove("a") - familyMap.match(family(comp) { has(1uL) }).shouldBeEmpty() + familyMap.match(family { has(1uL) }).shouldBeEmpty() } @Test fun addAndRemoveReplacingWithOther() { - val familyMap = Family2ObjectArrayMap(comp.types) + val familyMap = Family2ObjectArrayMap() familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) familyMap.add("b", EntityType(ulongArrayOf(1uL, 3uL))) - familyMap.match(family(comp) { has(1uL) }).shouldContainExactly("a", "b") + familyMap.match(family { has(1uL) }).shouldContainExactly("a", "b") familyMap.remove("a") - familyMap.match(family(comp) { + familyMap.match(family { has(1uL) }).shouldContainExactly("b") - familyMap.match(family(comp) { + familyMap.match(family { has(1uL) has(2uL) }).shouldContainExactly() @@ -48,13 +37,13 @@ class Family2ObjectArrayMapTest { @Test fun removeLastInArray() { - val familyMap = Family2ObjectArrayMap(comp.types) + val familyMap = Family2ObjectArrayMap() familyMap.add("a", EntityType(ulongArrayOf(1uL, 2uL, 3uL))) familyMap.add("b", EntityType(ulongArrayOf(1uL, 3uL))) - familyMap.match(family(comp) { has(1uL) }).shouldContainExactly("a", "b") + familyMap.match(family { has(1uL) }).shouldContainExactly("a", "b") familyMap.remove("a") familyMap.remove("b") - familyMap.match(family(comp) { + familyMap.match(family { has(1uL) }).shouldBeEmpty() } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt index 20ec236ba..f96dbeb14 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/FamilyTest.kt @@ -4,17 +4,17 @@ import com.mineinabyss.geary.datatypes.family.family import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.contains import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.relationOf import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test internal class FamilyTest: GearyTest() { private sealed class RelatesTo - val comp = MockComponentProvider() @Test fun contains() { - val family = family(comp) { + val family = family { has(1uL, 2uL, 3uL) } (EntityType(listOf(1uL, 2uL)) in family) shouldBe false @@ -30,11 +30,11 @@ internal class FamilyTest: GearyTest() { hasRelation(target) } - (EntityType(listOf(Relation.of(other).id)) in family) shouldBe false - (EntityType(listOf(Relation.of(target).id)) in family) shouldBe true + (EntityType(listOf(relationOf(other).id)) in family) shouldBe false + (EntityType(listOf(relationOf(target).id)) in family) shouldBe true // Archetypes will always have a non HOLDS_DATA version of a component present, but this alone should not succeed - (EntityType(listOf(Relation.of(target).id)) in family) shouldBe false - (EntityType(listOf(Relation.of(target).id, target.id)) in family) shouldBe true + (EntityType(listOf(relationOf(target).id)) in family) shouldBe false + (EntityType(listOf(relationOf(target).id, target.id)) in family) shouldBe true } @Test @@ -45,9 +45,9 @@ internal class FamilyTest: GearyTest() { hasRelation(target) } - (EntityType(listOf(Relation.of(target).id)) in family) shouldBe false - (EntityType(listOf(Relation.of(other).withRole(HOLDS_DATA).id)) in family) shouldBe false - (EntityType(listOf(Relation.of(target).withRole(HOLDS_DATA).id)) in family) shouldBe true + (EntityType(listOf(relationOf(target).id)) in family) shouldBe false + (EntityType(listOf(relationOf(other).withRole(HOLDS_DATA).id)) in family) shouldBe false + (EntityType(listOf(relationOf(target).withRole(HOLDS_DATA).id)) in family) shouldBe true } @Test @@ -64,8 +64,8 @@ internal class FamilyTest: GearyTest() { } } - (EntityType(listOf(Relation.of(target).id)) in family) shouldBe false - (EntityType(listOf(componentId(), Relation.of(target).id)) in family) shouldBe true + (EntityType(listOf(relationOf(target).id)) in family) shouldBe false + (EntityType(listOf(componentId(), relationOf(target).id)) in family) shouldBe true (EntityType(listOf(componentId())) in family) shouldBe false (EntityType(listOf(componentId())) in family) shouldBe false diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/GearyEntityTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/GearyEntityTests.kt index 5253261a4..df7a475ea 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/GearyEntityTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/datatypes/GearyEntityTests.kt @@ -2,9 +2,10 @@ package com.mineinabyss.geary.datatypes import com.mineinabyss.geary.components.relations.InstanceOf import com.mineinabyss.geary.helpers.* -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.archetypes +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.relationOf import com.mineinabyss.geary.systems.accessors.RelationWithData +import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe @@ -36,7 +37,7 @@ internal class GearyEntityTests : GearyTest() { entity { add() set(1) - }.type.getArchetype() shouldBe archetypes.archetypeProvider.rootArchetype + componentId() + componentId() + (HOLDS_DATA or componentId()) + }.type.getArchetype() shouldBe rootArchetype + componentId() + componentId() + (HOLDS_DATA or componentId()) } @Test @@ -44,7 +45,7 @@ internal class GearyEntityTests : GearyTest() { entity { set("Test") remove() - }.type.getArchetype() shouldBe archetypes.archetypeProvider.rootArchetype + }.type.getArchetype() shouldBe rootArchetype } @Test @@ -53,7 +54,7 @@ internal class GearyEntityTests : GearyTest() { set(1) set("Test") remove() - }.type.getArchetype() shouldBe archetypes.archetypeProvider.rootArchetype + componentId() + (HOLDS_DATA or componentId()) + }.type.getArchetype() shouldBe rootArchetype + componentId() + (HOLDS_DATA or componentId()) } @Test @@ -84,7 +85,7 @@ internal class GearyEntityTests : GearyTest() { entity { add() set("Test") - }.type.getArchetype() shouldBe archetypes.archetypeProvider.rootArchetype + componentId() + (componentId() or HOLDS_DATA) + }.type.getArchetype() shouldBe rootArchetype + componentId() + (componentId() or HOLDS_DATA) } @Test @@ -126,8 +127,8 @@ internal class GearyEntityTests : GearyTest() { setRelation("String to int relation") } entity.type.inner.shouldContainExactlyInAnyOrder( - Relation.of().id, - Relation.of().id, + relationOf().id, + relationOf().id, ) } @@ -141,7 +142,7 @@ internal class GearyEntityTests : GearyTest() { } entity.getAll().shouldContainExactlyInAnyOrder( "Test", - RelationWithData(RelatesTo(), null, relation = Relation.of().withRole(HOLDS_DATA)), + RelationWithData(RelatesTo(), null, relation = relationOf().withRole(HOLDS_DATA)), ) } @@ -163,8 +164,8 @@ internal class GearyEntityTests : GearyTest() { addParent(parent) } - parent.children.shouldContainExactly(child) - child.parents.shouldContainExactly(parent) + parent.children.shouldContain(child) + child.parents.shouldContain(parent) } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/ArchetypeTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/ArchetypeTest.kt index 983bef0a0..81b89214d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/ArchetypeTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/ArchetypeTest.kt @@ -3,12 +3,15 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.components.relations.InstanceOf import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.Relation +import com.mineinabyss.geary.datatypes.entityTypeOf +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap import com.mineinabyss.geary.engine.archetypes.Archetype +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider 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.get +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.relationOf import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Nested @@ -19,7 +22,7 @@ internal class ArchetypeTest : GearyTest() { @Nested inner class ArchetypeNavigation { - val root = archetypes.archetypeProvider.rootArchetype + val root = rootArchetype @Test fun `archetype ids assigned correctly`() { @@ -51,10 +54,10 @@ internal class ArchetypeTest : GearyTest() { fun matchedRelations() { val target = entity() val target2 = entity() - val relatesTo = Relation.of(target) - val instanceOf = Relation.of(target) - val instanceOf2 = Relation.of(target2) - val arc = Archetype(EntityType(listOf(relatesTo.id, instanceOf.id, instanceOf2.id)), 0) + val relatesTo = relationOf(target) + val instanceOf = relationOf(target) + val instanceOf2 = relationOf(target2) + val arc = get().getArchetype(entityTypeOf(relatesTo.id, instanceOf.id, instanceOf2.id)) arc.getRelationsByTarget(target.id).map { Relation.of(it) } .shouldContainExactlyInAnyOrder(relatesTo, instanceOf) arc.getRelationsByKind(componentId()).map { Relation.of(it) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/GearyEngineTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/GearyEngineTest.kt index 86865fd75..1ddbbdfc7 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/GearyEngineTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/engine/GearyEngineTest.kt @@ -1,9 +1,7 @@ package com.mineinabyss.geary.engine import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.helpers.toGeary -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -22,7 +20,7 @@ internal class GearyEngineTest : GearyTest() { entity().id shouldBe offset + 100uL (0 until 100).forEach { - geary.entityProvider.remove((offset + it.toULong()).toGeary()) + entityRemoveProvider.remove((offset + it.toULong())) } } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyEntityWithOperatorTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyEntityWithOperatorTest.kt index 6243a9896..9fd3cb587 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyEntityWithOperatorTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyEntityWithOperatorTest.kt @@ -1,6 +1,6 @@ package com.mineinabyss.geary.helpers -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTestTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTestTest.kt index d708f411f..02ab589a7 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTestTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTestTest.kt @@ -1,15 +1,28 @@ package com.mineinabyss.geary.helpers -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.datatypes.maps.ArrayTypeMap +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.modules.geary +import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import org.junit.jupiter.api.Test +import org.koin.dsl.koinApplication +import org.koin.dsl.module class GearyTestTest: GearyTest() { @Test fun `clear engine`() { - val engine = geary.engine + val engine = this.engine resetEngine() - geary.engine shouldNotBe engine + this.engine shouldNotBe engine + } + + @Test + // This is testing a behaviour of koin that I didn't expect, keep this here in case it changes in the future + fun `reusing koin modules should reuse exact class instances`() { + val module = module { + single { ArrayTypeMap() } + } + koinApplication { modules(module) }.koin.get() shouldBe koinApplication { modules(module) }.koin.get() } } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTypeFamilyHelpersTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTypeFamilyHelpersTest.kt index 80537d03b..9e8277a7d 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTypeFamilyHelpersTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/GearyTypeFamilyHelpersTest.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.helpers import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.datatypes.family.MutableFamily -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt index 4fd7f4137..e2f8242ab 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/helpers/tests/GearyTest.kt @@ -1,49 +1,13 @@ package com.mineinabyss.geary.helpers.tests +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.modules.get import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.withContext import org.junit.jupiter.api.AfterAll - -abstract class GearyTest { - private var _geary: Geary? = null - val geary get() = _geary!! - - init { - startEngine() - } - - fun startEngine() { - _geary = geary(TestEngineModule).start() - } - - @AfterAll - fun clearEngine() { - DI.clear() - _geary = null - } - - /** Recreates the engine. */ - fun resetEngine() { - clearEngine() - startEngine() - } - - companion object { - suspend inline fun concurrentOperation( - times: Int = 10000, - crossinline run: suspend (id: Int) -> T - ): List> { - return withContext(Dispatchers.Default) { - (0 until times).map { id -> - async { run(id) } - } - } - } - } -} +import org.koin.core.KoinApplication diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/instancing/InstancingTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/instancing/InstancingTest.kt index add1e1f7a..bc0789f78 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/instancing/InstancingTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/instancing/InstancingTest.kt @@ -1,12 +1,11 @@ package com.mineinabyss.geary.instancing import com.mineinabyss.geary.components.relations.NoInherit -import com.mineinabyss.geary.datatypes.Relation import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.observe +import com.mineinabyss.geary.modules.relationOf import com.mineinabyss.geary.observers.events.OnSet -import com.mineinabyss.geary.systems.builders.observe import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe import org.junit.jupiter.api.BeforeEach @@ -30,7 +29,7 @@ class InstancingTest : GearyTest() { val instance = entity { extend(prefab) } // assert - instance.getRelations().shouldBe(listOf(Relation.of(relatesTo))) + instance.getRelations().shouldBe(listOf(relationOf(relatesTo))) } @Test @@ -68,7 +67,7 @@ class InstancingTest : GearyTest() { @Test fun `should correctly extend entity when listener modifies it during extend process`() { // arrange - geary.observe().involving().exec { + observe().involving().exec { entity.set("Modifying!") } val prefab = entity { diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/koin/ArchetypeEngineModuleCheck.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/koin/ArchetypeEngineModuleCheck.kt new file mode 100644 index 000000000..d6edd4663 --- /dev/null +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/koin/ArchetypeEngineModuleCheck.kt @@ -0,0 +1,40 @@ +package com.mineinabyss.geary.koin + +import co.touchlab.kermit.LoggerConfig +import com.mineinabyss.geary.engine.archetypes.operations.ArchetypeReadOperations +import com.mineinabyss.geary.modules.ArchetypeEngineModule +import com.mineinabyss.geary.modules.ArchetypesModules +import com.mineinabyss.geary.modules.TestEngineModule +import com.mineinabyss.geary.modules.geary +import org.junit.jupiter.api.Test +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.module.Module +import org.koin.dsl.koinApplication +import org.koin.test.check.checkModules +import org.koin.test.verify.verify +import kotlin.coroutines.CoroutineContext +import kotlin.time.Duration + +class ArchetypeEngineModuleCheck { + @OptIn(KoinExperimentalAPI::class) + @Test + fun checkKoinModule() { + ArchetypeEngineModule().module.verify( + extraTypes = listOf(Boolean::class, Duration::class, LoggerConfig::class, CoroutineContext::class) + ) + } + + @Test + fun createKoinModule() { + koinApplication { + properties(ArchetypeEngineModule().properties) + modules(ArchetypeEngineModule().module) + checkModules() + } + } + + @Test + fun startGeary() { + geary(TestEngineModule).start() + } +} diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/EntityRemoveObserverTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/EntityRemoveObserverTest.kt index de8bd2a03..702911d13 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/EntityRemoveObserverTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/EntityRemoveObserverTest.kt @@ -1,10 +1,9 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnEntityRemoved -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query import io.kotest.matchers.shouldBe import kotlin.test.Test @@ -14,13 +13,13 @@ class EntityRemoveObserverTest : GearyTest() { fun `should correctly run multiple listeners on single event`() { var called = 0 - val listener1 = geary.observe().exec(query()) { (data) -> + val listener1 = observe().exec(query()) { (data) -> data shouldBe 1 entity.remove() called++ } - val listener2 = geary.observe().exec(query()) { (data) -> + val listener2 = observe().exec(query()) { (data) -> data shouldBe "" } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserveComponentEventsTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserveComponentEventsTests.kt index a82f12c59..292a1c480 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserveComponentEventsTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserveComponentEventsTests.kt @@ -2,12 +2,11 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnAdd import com.mineinabyss.geary.observers.events.OnRemove import com.mineinabyss.geary.observers.events.OnSet -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query import io.kotest.assertions.assertSoftly import io.kotest.matchers.collections.shouldContainExactly @@ -28,7 +27,7 @@ internal class ObserveComponentEventsTests : GearyTest() { @Test fun `should correctly observe OnSet events involving single component`() { var called = 0 - geary.observe().involving().exec { called += 1 } + observe().involving().exec { called += 1 } val entity = entity() called shouldBe 0 @@ -42,7 +41,7 @@ internal class ObserveComponentEventsTests : GearyTest() { fun `should correctly observe OnSet events involving multiple components`() { var called = 0 - geary.observe().involving(query()).exec { called++ } + observe().involving(query()).exec { called++ } entity { set("") @@ -63,7 +62,7 @@ internal class ObserveComponentEventsTests : GearyTest() { @Test fun `should observe add event when component added or set`() { var called = 0 - geary.observe().involving().exec { + observe().involving().exec { called += 1 } @@ -81,7 +80,7 @@ internal class ObserveComponentEventsTests : GearyTest() { fun `should fire remove listener and have access to removed component data when component removed`() { // arrange var called = 0 - geary.observe().involving().exec(query()) { (string) -> + observe().involving().exec(query()) { (string) -> string shouldBe "data" called++ } @@ -102,7 +101,7 @@ internal class ObserveComponentEventsTests : GearyTest() { var called = 0 - geary.observe().involving().exec { + observe().involving().exec { called++ } val entity = entity { @@ -121,7 +120,7 @@ internal class ObserveComponentEventsTests : GearyTest() { fun `should not call remove listener when added but not set component removed`() { // arrange var called = 0 - geary.observe().involving(query()).exec { + observe().involving(query()).exec { called++ } val entity = entity { @@ -139,7 +138,7 @@ internal class ObserveComponentEventsTests : GearyTest() { fun `should still remove component after remove listener modifies it`() { // arrange var called = 0 - geary.observe().involving(query()).exec { + observe().involving(query()).exec { called++ entity.set("new data") } @@ -161,7 +160,7 @@ internal class ObserveComponentEventsTests : GearyTest() { fun `should correctly fire listener that listens to several removed components`() { // arrange var called = mutableListOf() - geary.observe().involving(query()).exec { (string, int) -> + observe().involving(query()).exec { (string, int) -> called.add(entity) } val entity1 = entity { @@ -182,14 +181,14 @@ internal class ObserveComponentEventsTests : GearyTest() { entity3.remove() // Doesn't fire // assert - called shouldContainExactly listOf(entity1, entity2) + called shouldContainExactly listOf(entity1, entity2) } @Test fun `should correctly remove component if event listener modifies type`() { // arrange var called = 0 - geary.observe().involving(query()).exec { (string) -> + observe().involving(query()).exec { (string) -> called++ entity.set(1) } diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserverTypeTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserverTypeTests.kt index a551a811b..16a437cf8 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserverTypeTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/ObserverTypeTests.kt @@ -2,10 +2,10 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.systems.builders.observe -import com.mineinabyss.geary.systems.builders.observeWithData +import com.mineinabyss.geary.modules.observe +import com.mineinabyss.geary.modules.observeWithData import com.mineinabyss.geary.systems.query.query import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf @@ -22,7 +22,7 @@ class ObserverTypeTests : GearyTest() { @Test fun `should observe event regardless of holding data when not observing event data`() { var called = 0 - geary.observe().exec { called++ } + observe().exec { called++ } val entity = entity() called shouldBe 0 @@ -37,7 +37,7 @@ class ObserverTypeTests : GearyTest() { @Test fun `should not observe event without data when observing data`() { var called = 0 - geary.observeWithData().exec { + observeWithData().exec { event.shouldBeInstanceOf() called++ } @@ -55,7 +55,7 @@ class ObserverTypeTests : GearyTest() { @Test fun `should observe events involving component when filtering one involved component`() { var called = 0 - geary.observe().involving(query()).exec { (int) -> called += int } + observe().involving(query()).exec { (int) -> called += int } val entity = entity() diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/QueryAsMapTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/QueryAsMapTest.kt index ab2ab679c..7e45d4cd7 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/QueryAsMapTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/QueryAsMapTest.kt @@ -1,7 +1,7 @@ package com.mineinabyss.geary.observers import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.observers.queries.cacheAssociatedBy import com.mineinabyss.geary.systems.query.query @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test class QueryAsMapTest : GearyTest() { @Test fun `should correctly track entity in map`() { - val map = geary.cacheAssociatedBy(query()) { (string) -> string } + val map = cacheAssociatedBy(query()) { (string) -> string } entity { set("Hello world") diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/entity_scoped/EntityScopedObserverTest.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/entity_scoped/EntityScopedObserverTest.kt index ec5b73b7f..cd93e3472 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/entity_scoped/EntityScopedObserverTest.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/observers/entity_scoped/EntityScopedObserverTest.kt @@ -1,10 +1,9 @@ package com.mineinabyss.geary.observers.entity_scoped import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.test.GearyTest +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.entity.observe -import com.mineinabyss.geary.systems.builders.observe import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test @@ -18,7 +17,7 @@ class EntityScopedObserverTest : GearyTest() { val globallyObserve = entity() val lines = mutableListOf() scopedObserve.observe().exec { lines += "Scoped clicked $entity" } - geary.observe().exec { lines += "Global clicked $entity" } + observe().exec { lines += "Global clicked $entity" } // act scopedObserve.emit() 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 a59d2db00..e68359bc5 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,7 +2,7 @@ package com.mineinabyss.geary.queries import com.mineinabyss.geary.annotations.optin.ExperimentalGearyApi import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.helpers.tests.GearyTest +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.query.Query import com.mineinabyss.geary.systems.query.query @@ -35,7 +35,7 @@ class SimpleQueryTest : GearyTest() { @Test fun `forEach should allow reading data`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) val nums = mutableListOf() query.forEach { (int) -> @@ -46,7 +46,7 @@ class SimpleQueryTest : GearyTest() { @Test fun `entities should return matched entities correctly`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) val nums = mutableListOf() query.entities().forEach { @@ -58,14 +58,14 @@ class SimpleQueryTest : GearyTest() { @Test fun `first should correctly return first matched entity`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) query.find({ it.comp1 }, { it.comp1 == 5 }) shouldBe 5 } @Test fun `any should correctly check for matches if matched`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) query.any { it.comp1 == 5 } shouldBe true query.any { it.comp1 == 100 } shouldBe false @@ -73,7 +73,7 @@ class SimpleQueryTest : GearyTest() { @Test fun `should be able to collect query as sequence`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) query.collect { filter { it.comp1 % 2 == 0 }.map { it.comp1.toString() }.toList() @@ -82,7 +82,7 @@ class SimpleQueryTest : GearyTest() { @Test fun `should allow collecting fancier sequences`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) query.collect { filter { it.comp1 % 2 == 0 }.map { it.comp1 }.sortedByDescending { it }.take(3).toList() @@ -91,7 +91,7 @@ class SimpleQueryTest : GearyTest() { @Test fun `should not allow working on sequence outside collect block`() { - val query = geary.queryManager.trackQuery(myQuery) + val query = queryManager.trackQuery(myQuery) shouldThrow { query.collect { @@ -109,7 +109,7 @@ class SimpleQueryTest : GearyTest() { entity { set("Only string") } - val query = geary.queryManager.trackQuery(query()) + val query = queryManager.trackQuery(query()) val matched = query.toList() assertSoftly(matched) { shouldContainAll((0..9 step 2).map { it to null }) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/AccessorDataModificationTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/AccessorDataModificationTests.kt index b26c59f8e..c4b1e0d39 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/AccessorDataModificationTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/AccessorDataModificationTests.kt @@ -1,17 +1,14 @@ package com.mineinabyss.geary.queries.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.builders.cache +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.shouldBe import kotlin.test.Test class AccessorDataModificationTests : GearyTest() { - private fun registerQuery() = geary.cache(object : Query() { + private fun registerQuery() = cache(object : Query(this) { var data by get() }) diff --git a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/MappedAccessorTests.kt b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/MappedAccessorTests.kt index 56bd0dee3..85a7a0780 100644 --- a/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/MappedAccessorTests.kt +++ b/geary-core/src/jvmTest/kotlin/com/mineinabyss/geary/queries/accessors/MappedAccessorTests.kt @@ -1,9 +1,7 @@ package com.mineinabyss.geary.queries.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.cache +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.shouldBe @@ -12,11 +10,11 @@ import org.junit.jupiter.api.Test internal class MappedAccessorTests : GearyTest() { private class Marker - private fun mappedQuery() = geary.cache(object : Query() { + private fun mappedQuery() = cache(object : Query(this) { val mapped by get().map { it.toString() } }) - private fun defaultingQuery() = geary.cache(object : Query() { + private fun defaultingQuery() = cache(object : Query(this) { val default by get().orDefault { "empty!" } override fun ensure() = this { has() } }) 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 28c01058a..d4c7e2c5c 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 @@ -3,8 +3,8 @@ package com.mineinabyss.geary.systems 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.findEntities +import com.mineinabyss.geary.test.GearyTest import com.mineinabyss.geary.systems.query.Query import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainAll @@ -15,7 +15,7 @@ class FamilyMatchingTest : GearyTest() { val stringId = componentId() or HOLDS_DATA val intId = componentId() - val system = geary.system(object : Query() { + val system = system(object : Query(this) { val string by get() override fun ensure() = this { has() } }).defer { it.string }.onFinish { data, entity -> @@ -23,7 +23,7 @@ class FamilyMatchingTest : GearyTest() { entity.has() shouldBe true } - val root = archetypes.archetypeProvider.rootArchetype + val root = rootArchetype val correctArchetype = root + stringId + intId @Test @@ -41,7 +41,8 @@ class FamilyMatchingTest : GearyTest() { set("Test") set(1) } - geary.queryManager.getEntitiesMatching(system.runner.family).shouldContainAll(entity, entity2) + val entities = findEntities(system.runner.family) + entities.shouldContainAll(entity, entity2) } @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 30a116ea5..644300f0f 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 @@ -1,12 +1,12 @@ package com.mineinabyss.geary.systems import com.mineinabyss.geary.components.relations.InstanceOf +import com.mineinabyss.geary.helpers.componentId 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.findEntities +import com.mineinabyss.geary.test.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 @@ -24,7 +24,7 @@ class RelationMatchingSystemTest : GearyTest() { fun relations() { var ran = 0 resetEngine() - val system = geary.system(object : Query() { + val system = system(object : Query(this) { val persists by getRelationsWithData() }).exec { ran++ @@ -57,7 +57,7 @@ class RelationMatchingSystemTest : GearyTest() { var ran = 0 var persistsCount = 0 var instanceOfCount = 0 - val system = geary.system(object : Query() { + val system = system(object : Query(this) { val persists by getRelationsWithData() val instanceOf by getRelationsWithData() }).exec { @@ -99,7 +99,6 @@ class RelationMatchingSystemTest : GearyTest() { fun relationsWithData() { resetEngine() - val entity = entity { setRelation(Persists()) add() @@ -110,16 +109,19 @@ class RelationMatchingSystemTest : GearyTest() { set("Test") } - val system = geary.system(object : Query() { + val system = system(object : Query(this) { val withData by getRelationsWithData() }).exec { withData.forAll { it.data shouldBe Persists() } withData.forAll { it.targetData shouldBe "Test" } } - + println(componentId()) + println(findEntities { + hasRelation() + }.toList()) system.runner.matchedArchetypes.shouldNotContain(entity.type.getArchetype()) system.runner.matchedArchetypes.shouldContain(entityWithData.type.getArchetype()) - geary.engine.tick(0) + engine.tick(0) } } diff --git a/geary-test/build.gradle.kts b/geary-test/build.gradle.kts new file mode 100644 index 000000000..8f7e8b3e9 --- /dev/null +++ b/geary-test/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id(idofrontLibs.plugins.mia.kotlin.jvm.get().pluginId) + id(idofrontLibs.plugins.mia.publication.get().pluginId) + alias(idofrontLibs.plugins.kotlinx.serialization) +} + +dependencies { + implementation(project(":geary-core")) + implementation(kotlin("test")) + implementation(idofrontLibs.kotlinx.coroutines.test) + implementation(libs.koin.test) + compileOnly(idofrontLibs.junit.jupiter) +} diff --git a/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt b/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt new file mode 100644 index 000000000..d6e5832b6 --- /dev/null +++ b/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt @@ -0,0 +1,58 @@ +package com.mineinabyss.geary.test + +import com.mineinabyss.geary.engine.Engine +import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.TestEngineModule +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.get +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.withContext +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.TestInstance +import org.koin.core.KoinApplication + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +abstract class GearyTest : Geary { + private var _application: KoinApplication? = null + override val application get() = _application!! + val rootArchetype get() = get().rootArchetype + + open fun setupGeary() = geary(TestEngineModule) + + init { + startEngine() + } + + fun startEngine() { + println(setupGeary().start().application.koin.get()) + println(setupGeary().start().application.koin.get()) + _application = setupGeary().start().application + } + + @AfterAll + fun clearEngine() { + _application = null + } + + /** Recreates the engine. */ + fun resetEngine() { + clearEngine() + startEngine() + } + + companion object { + suspend inline fun concurrentOperation( + times: Int = 10000, + crossinline run: suspend (id: Int) -> T, + ): List> { + return withContext(Dispatchers.Default) { + (0 until times).map { id -> + async { run(id) } + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties index d28ee949a..407c8c1b4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,5 @@ group=com.mineinabyss 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.25.3 +idofrontVersion=0.25.13 kotlin.native.ignoreDisabledTargets=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 08dfbc807..9fc848ee6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,8 @@ kotlinxBenchmarkRuntime = "0.4.10" okio = "3.9.0" roaringbitmap = "1.0.6" statelyConcurrency = "2.0.7" -uuid = "0.8.4" +koin = "4.0.0" +junitJupiter = "5.8.1" [libraries] androidx-collection = { module = "androidx.collection:collection", version.ref = "androidxCollection" } @@ -14,4 +15,6 @@ kotlinx-benchmark-runtime = { module = "org.jetbrains.kotlinx:kotlinx-benchmark- okio = { module = "com.squareup.okio:okio", version.ref = "okio" } roaringbitmap = { module = "org.roaringbitmap:RoaringBitmap", version.ref = "roaringbitmap" } stately-concurrency = { module = "co.touchlab:stately-concurrency", version.ref = "statelyConcurrency" } -uuid = { module = "com.benasher44:uuid", version.ref = "uuid" } +koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } +koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junitJupiter" } diff --git a/settings.gradle.kts b/settings.gradle.kts index eb2b95f4d..f625799c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,8 +26,9 @@ dependencyResolutionManagement { } include( - "geary-benchmarks", +// "geary-benchmarks", "geary-core", + "geary-test", ) // Go through addons directory and load all projects based on file name From c30d09bf29c40f116c39295c365e78822051104f Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 16 Oct 2024 16:46:24 -0400 Subject: [PATCH 3/9] refactor: Get addons compiling --- .../mineinabyss/geary/actions/ActionGroup.kt | 16 ++++++------ .../geary/actions/actions/EmitEventAction.kt | 5 ++-- .../geary/actions/actions/EnsureAction.kt | 15 ++++++++--- .../geary/actions/actions/EvalAction.kt | 8 +++--- .../actions/event_binds/EntityObservers.kt | 25 ++++++++++++------- .../event_binds/ParseEntityObservers.kt | 3 ++- .../geary/actions/event_binds/Passive.kt | 3 ++- .../geary/actions/expressions/Expression.kt | 18 ++++++++----- .../actions/expressions/FunctionExpression.kt | 9 ++++--- .../expressions/InlineExpressionSerializer.kt | 7 ++++-- .../actions/ConfigEntityObserversTests.kt | 2 +- .../geary/actions/ExpressionDecodingTest.kt | 15 +++++------ .../mineinabyss/geary/prefabs/PrefabKey.kt | 10 ++++---- .../mineinabyss/geary/prefabs/PrefabLoader.kt | 24 +++++++++--------- .../mineinabyss/geary/prefabs/PrefabsDSL.kt | 5 ++-- .../geary/prefabs/PrefabsModule.kt | 14 ++++++++--- .../{FileSystemAddon.kt => FileSystem.kt} | 2 +- .../geary/addons/dsl/GearyAddon.kt | 2 ++ .../com/mineinabyss/geary/modules/Geary.kt | 1 + .../com/mineinabyss/geary/test/GearyTest.kt | 15 ++++------- 20 files changed, 117 insertions(+), 82 deletions(-) rename addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/{FileSystemAddon.kt => FileSystem.kt} (83%) diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt index 07a3e30ee..9a801b8ef 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt @@ -1,15 +1,14 @@ package com.mineinabyss.geary.actions -import co.touchlab.kermit.Logger import com.mineinabyss.geary.actions.actions.EmitEventAction import com.mineinabyss.geary.actions.actions.EnsureAction import com.mineinabyss.geary.actions.event_binds.* import com.mineinabyss.geary.actions.expressions.Expression import com.mineinabyss.geary.modules.Geary -import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.SerializedComponents +import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer @@ -22,7 +21,8 @@ class ActionEntry( val environment: Map>?, ) -@Serializable(with = ActionGroup.Serializer::class) +@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") +@Serializable(with = ContextualSerializer::class) class ActionGroup( val actions: List, ) : Action { @@ -58,7 +58,9 @@ class ActionGroup( context.register(entry.register, returned) } - class Serializer : InnerSerializer, ActionGroup>( + class Serializer( + val world: Geary + ) : InnerSerializer, ActionGroup>( serialName = "geary:action_group", inner = ListSerializer( PolymorphicListAsMapSerializer.ofComponents( @@ -66,7 +68,7 @@ class ActionGroup( customKeys = mapOf( "when" to { ActionWhen.serializer() }, "register" to { ActionRegister.serializer() }, - "onFail" to { ActionOnFail.serializer() }, + "onFail" to { ContextualSerializer(ActionOnFail::class) }, "loop" to { ActionLoop.serializer() } ) ) @@ -87,11 +89,11 @@ class ActionGroup( comp is ActionRegister -> register = comp.register comp is ActionOnFail -> onFail = comp.action comp is ActionLoop -> loop = - Expression.parseExpression(comp.expression, serializersModule) as Expression> + Expression.parseExpression(world, comp.expression, serializersModule) as Expression> comp is ActionEnvironment -> environment = comp.environment action != null -> Geary.w { "Multiple actions defined in one block!" } - else -> action = EmitEventAction.wrapIfNotAction(comp) + else -> action = EmitEventAction.wrapIfNotAction(world, comp) } } if (action == null) return@mapNotNull null diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EmitEventAction.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EmitEventAction.kt index 8ccd52cf8..21d380a30 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EmitEventAction.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EmitEventAction.kt @@ -4,6 +4,7 @@ import com.mineinabyss.geary.actions.Action import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.modules.Geary class EmitEventAction( val eventId: ComponentId, @@ -14,8 +15,8 @@ class EmitEventAction( } companion object { - fun from(data: Any) = EmitEventAction(componentId(data::class), data) + fun from(world: Geary, data: Any) = EmitEventAction(world.componentId(data::class), data) - fun wrapIfNotAction(data: Any) = if (data is Action) data else from(data) + fun wrapIfNotAction(world: Geary, data: Any) = if (data is Action) data else from(world, data) } } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt index 95a2f895f..a0e597019 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt @@ -2,18 +2,23 @@ package com.mineinabyss.geary.actions.actions import com.mineinabyss.geary.actions.* import com.mineinabyss.geary.helpers.componentId +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.SerializedComponents +import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -@Serializable(with = EnsureAction.Serializer::class) +//@Serializable(with = EnsureAction.Serializer::class) +@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") +@Serializable(with = ContextualSerializer::class) class EnsureAction( + world: Geary, val conditions: SerializedComponents, ) : Action { @Transient - private val flat = conditions.map { componentId(it::class) to it } + private val flat = conditions.map { world.componentId(it::class) to it } override fun ActionGroupContext.execute() { flat.forEach { (id, data) -> @@ -38,10 +43,12 @@ class EnsureAction( return true } - object Serializer : InnerSerializer( + class Serializer( + val world: Geary + ) : InnerSerializer( serialName = "geary:ensure", inner = PolymorphicListAsMapSerializer.ofComponents(), inverseTransform = { it.conditions }, - transform = { EnsureAction(it) } + transform = { EnsureAction(world, it) } ) } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EvalAction.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EvalAction.kt index f11e0221d..915e1b89a 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EvalAction.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EvalAction.kt @@ -4,19 +4,21 @@ import com.mineinabyss.geary.actions.Action import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.expressions.Expression import com.mineinabyss.geary.actions.expressions.InlineExpressionSerializer +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import kotlinx.serialization.Serializable -@Serializable(with = EvalAction.Serializer::class) class EvalAction( val expression: Expression<*>, ) : Action { override fun ActionGroupContext.execute() = expression.evaluate(this) - object Serializer : InnerSerializer, EvalAction>( + class Serializer( + world: Geary, + ) : InnerSerializer, EvalAction>( serialName = "geary:eval", - inner = InlineExpressionSerializer, + inner = InlineExpressionSerializer(world), inverseTransform = { it.expression }, transform = { EvalAction(it) } ) diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt index e92a9d4ca..e86491787 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt @@ -3,25 +3,30 @@ package com.mineinabyss.geary.actions.event_binds import com.mineinabyss.geary.actions.ActionGroup import com.mineinabyss.geary.actions.actions.EnsureAction import com.mineinabyss.geary.actions.expressions.Expression +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.SerializableComponentId import kotlinx.serialization.Contextual import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.UseContextualSerialization import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlin.jvm.JvmInline -@Serializable(with = EntityObservers.Serializer::class) +@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") +@Serializable(with = ContextualSerializer::class) class EntityObservers( val observers: List, ) { - class Serializer : InnerSerializer, EntityObservers>( + class Serializer( + world: Geary + ) : InnerSerializer, EntityObservers>( serialName = "geary:observe", inner = MapSerializer( SerializableComponentId.serializer(), - ActionGroup.serializer() + ActionGroup.Serializer(world) ), inverseTransform = { TODO() }, transform = { @@ -49,11 +54,12 @@ class ActionWhen(val conditions: List) { @Serializable value class ActionRegister(val register: String) -@Serializable(with = ActionOnFail.Serializer::class) class ActionOnFail(val action: ActionGroup) { - class Serializer : InnerSerializer( + class Serializer( + world: Geary + ) : InnerSerializer( serialName = "geary:on_fail", - inner = ActionGroup.Serializer(), + inner = ActionGroup.Serializer(world), inverseTransform = ActionOnFail::action, transform = { ActionOnFail(it) } ) @@ -63,11 +69,12 @@ class ActionOnFail(val action: ActionGroup) { @Serializable value class ActionLoop(val expression: String) -@Serializable(with = ActionEnvironment.Serializer::class) class ActionEnvironment(val environment: Map>) { - object Serializer : InnerSerializer>, ActionEnvironment>( + class Serializer( + world: Geary, + ) : InnerSerializer>, ActionEnvironment>( serialName = "geary:with", - inner = MapSerializer(String.serializer(), Expression.serializer(ContextualSerializer(Any::class))), + inner = MapSerializer(String.serializer(), Expression.Serializer(world, ContextualSerializer(Any::class))), inverseTransform = ActionEnvironment::environment, transform = { ActionEnvironment(it) } ) diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt index a8853489e..b98071710 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/ParseEntityObservers.kt @@ -4,6 +4,7 @@ import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.execute import com.mineinabyss.geary.datatypes.EntityType import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.entity.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.systems.query.query @@ -13,7 +14,7 @@ fun Geary.bindEntityObservers() = observe() .exec { (observers) -> observers.observers.forEach { observer -> val actionGroup = observer.actionGroup - entity.observe(observer.event.id).involving(EntityType(observer.involving.map { it.id })).exec { + entity.observe(observer.event).involving(EntityType(observer.involving)).exec { val context = ActionGroupContext(entity) actionGroup.execute(context) } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt index 6991f3dcd..2971b2f2c 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/Passive.kt @@ -7,6 +7,7 @@ import com.mineinabyss.geary.actions.serializers.DurationSerializer import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.fastForEach import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.SerializableComponentId @@ -43,7 +44,7 @@ fun Geary.parsePassive() = observe() entity.add(systemMatchingId) system(query { has(systemMatchingId) - has(systemBind.match.map { it.id }) + has(systemBind.match) }).every(systemBind.every).execOnAll { entities().fastForEach { entity -> runCatching { diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt index 9e37193b3..497edf976 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt @@ -1,6 +1,7 @@ package com.mineinabyss.geary.actions.expressions import com.mineinabyss.geary.actions.ActionGroupContext +import com.mineinabyss.geary.modules.Geary import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -12,7 +13,6 @@ import kotlinx.serialization.encoding.decodeStructure import kotlinx.serialization.modules.SerializersModule import kotlin.math.min -@Serializable(with = Expression.Serializer::class) sealed interface Expression { fun evaluate(context: ActionGroupContext): T data class Fixed( @@ -31,23 +31,25 @@ sealed interface Expression { } companion object { - fun parseExpression(string: String, module: SerializersModule): Expression<*> { + fun parseExpression( + world: Geary,string: String, module: SerializersModule): Expression<*> { val (name, rem) = getFunctionName(string) val reference = Variable(name) if(rem == "") return reference - return foldFunctions(reference, rem, module) + return foldFunctions(world, reference, rem, module) } tailrec fun foldFunctions( + world: Geary, reference: Expression<*>, remainder: String, module: SerializersModule, ): Expression<*> { val (name, afterName) = getFunctionName(remainder) val (yaml, afterYaml) = getYaml(afterName) - val functionExpr = FunctionExpression.parse(reference, name, yaml, module) + val functionExpr = FunctionExpression.parse(world, reference, name, yaml, module) if (afterYaml == "") return functionExpr - return foldFunctions(functionExpr, afterYaml, module) + return foldFunctions(world, functionExpr, afterYaml, module) } fun getYaml(expr: String): Pair { @@ -72,7 +74,10 @@ sealed interface Expression { // TODO kaml handles contextual completely different form Json, can we somehow allow both? Otherwise // kaml also has broken contextual serializer support that we need to work around :( - class Serializer(val serializer: KSerializer) : KSerializer> { + class Serializer( + val world: Geary, + val serializer: KSerializer + ) : KSerializer> { override val descriptor: SerialDescriptor = ContextualSerializer(Any::class).descriptor override fun deserialize(decoder: Decoder): Expression { @@ -84,6 +89,7 @@ sealed interface Expression { }.onSuccess { string -> if (string.startsWith("{{") && string.endsWith("}}")) return parseExpression( + world, string.removePrefix("{{").removeSuffix("}}").trim(), decoder.serializersModule ) as Expression diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt index 6aac9fa55..37270064a 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt @@ -1,19 +1,22 @@ package com.mineinabyss.geary.actions.expressions import com.mineinabyss.geary.actions.ActionGroupContext -import com.mineinabyss.geary.serialization.serializableComponents -import com.mineinabyss.geary.serialization.serializers.SerializableComponentId +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.SerializableComponents +import com.mineinabyss.geary.serialization.serializers.ComponentIdSerializer import kotlinx.serialization.modules.SerializersModule interface FunctionExpression { companion object { fun parse( + world: Geary, ref: Expression<*>, name: String, yaml: String, module: SerializersModule, ): FunctionExpressionWithInput<*, *> { - val compClass = SerializableComponentId.Serializer.getComponent(name, module) + val serializableComponents = world.getAddon(SerializableComponents) + val compClass = ComponentIdSerializer(world).getComponent(name, module) val serializer = serializableComponents.serializers.getSerializerFor(compClass) ?: error("No serializer found for component $name") val expr = diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/InlineExpressionSerializer.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/InlineExpressionSerializer.kt index 0d2b609a0..7a22998cf 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/InlineExpressionSerializer.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/InlineExpressionSerializer.kt @@ -1,16 +1,20 @@ package com.mineinabyss.geary.actions.expressions +import com.mineinabyss.geary.modules.Geary import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -object InlineExpressionSerializer : KSerializer> { +class InlineExpressionSerializer( + val world: Geary, +) : KSerializer> { override val descriptor: SerialDescriptor = String.serializer().descriptor override fun deserialize(decoder: Decoder): Expression<*> { return Expression.parseExpression( + world, decoder.decodeString(), decoder.serializersModule ) @@ -19,5 +23,4 @@ object InlineExpressionSerializer : KSerializer> { override fun serialize(encoder: Encoder, value: Expression<*>) { TODO("Not yet implemented") } - } diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt index c7021443e..abbbd2a5b 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt @@ -9,12 +9,12 @@ import com.mineinabyss.geary.serialization.dsl.withCommonComponentNames import com.mineinabyss.geary.serialization.formats.YamlFormat import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer import org.junit.jupiter.api.Test -import test.GearyTest class ConfigEntityObserversTests : GearyTest() { @Serializable diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt index 48f32e1a0..abbd24f4b 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt @@ -7,16 +7,16 @@ import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.serialization.dsl.serialization import com.mineinabyss.geary.serialization.formats.YamlFormat -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.serialization.serialization +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.shouldBe import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer import org.junit.jupiter.api.Test -class ExpressionDecodingTest { +class ExpressionDecodingTest : GearyTest() { @Serializable data class TestData( val name: Expression, @@ -66,20 +66,17 @@ class ExpressionDecodingTest { @Test fun shouldCorrectlyParseExpressionFunctions() { - DI.clear() - geary(TestEngineModule){ + resetEngine(geary(TestEngineModule) { serialization { components { component(TestFunction.serializer()) } format("yml", ::YamlFormat) } + }) - } - - geary.pipeline.runStartupTasks() val input = "'{{ entity.geary:testFunction{ string: test } }}'" - val expr = Yaml.default.decodeFromString(Expression.Serializer(String.serializer()), input) + val expr = Yaml.default.decodeFromString(Expression.Serializer(this, String.serializer()), input) expr.evaluate(ActionGroupContext(entity = entity())) shouldBe "test" } } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt index f5b0e119d..02e003998 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt @@ -1,6 +1,7 @@ package com.mineinabyss.geary.prefabs import com.mineinabyss.geary.datatypes.Entity +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.prefabs.serializers.PrefabKeySerializer import kotlinx.serialization.Serializable @@ -11,11 +12,6 @@ import kotlinx.serialization.Serializable @Serializable(with = PrefabKeySerializer::class) // We don't make this a value class since calculating substring is pretty expensive compared to one new object instantiation data class PrefabKey private constructor(val namespace: String, val key: String) { - fun toEntity(): Entity = toEntityOrNull() - ?: error("Requested non null prefab entity for $this, but it does not exist.") - - fun toEntityOrNull(): Entity? = prefabs.manager[this] - val full get() = "$namespace:$key" override fun toString(): String = full @@ -37,3 +33,7 @@ data class PrefabKey private constructor(val namespace: String, val key: String) } } +fun Geary.entityOfOrNull(key: PrefabKey): Entity = entityOf(key) + ?: error("Requested non null prefab entity for $this, but it does not exist.") + +fun Geary.entityOf(key: PrefabKey): Entity? = getAddon(Prefabs).manager[key] diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt index 821612563..6a80d3222 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt @@ -1,39 +1,39 @@ package com.mineinabyss.geary.prefabs +import co.touchlab.kermit.Logger import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.helpers.fastForEach -import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances import com.mineinabyss.geary.prefabs.configuration.components.InheritPrefabs import com.mineinabyss.geary.prefabs.configuration.components.Prefab import com.mineinabyss.geary.prefabs.helpers.inheritPrefabsIfNeeded import com.mineinabyss.geary.serialization.formats.Format.ConfigType.NON_STRICT -import com.mineinabyss.geary.serialization.serializableComponents +import com.mineinabyss.geary.serialization.formats.Formats import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer.Companion.provideConfig -import com.mineinabyss.geary.systems.builders.cache import com.mineinabyss.geary.systems.query.Query import kotlinx.serialization.Serializable import kotlinx.serialization.modules.SerializersModule import okio.Path import kotlin.uuid.Uuid -class PrefabLoader { - private val formats get() = serializableComponents.formats - - private val logger get() = geary.logger - +class PrefabLoader( + val world: Geary, + val formats: Formats, + val logger: Logger, +) { private val readFiles = mutableListOf() - private val needsInherit = geary.cache(NeedsInherit()) + private val needsInherit = world.cache(NeedsInherit(world)) fun addSource(path: PrefabPath) { readFiles.add(path) } - class NeedsInherit : Query() { + class NeedsInherit(world: Geary) : Query(world) { val inheritPrefabs by get() } @@ -113,7 +113,7 @@ class PrefabLoader { return PrefabLoadResult.Failure(exception) } - val entity = writeTo ?: entity() + val entity = writeTo ?: world.entity() entity.addRelation() entity.addRelation() entity.addRelation() @@ -128,7 +128,7 @@ class PrefabLoader { fun loadFromPathOrReloadExisting(namespace: String, path: Path): PrefabLoadResult { val key = PrefabKey.of(namespace, path.name.substringBeforeLast('.')) - val existing = prefabs.manager[key] + val existing = world.getAddon(Prefabs).manager[key] existing?.clear() return loadFromPath(namespace, path, existing) } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt index d0f1e75d9..0021d5a7b 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt @@ -2,13 +2,14 @@ package com.mineinabyss.geary.prefabs import com.mineinabyss.geary.addons.Namespaced import com.mineinabyss.geary.addons.dsl.GearyDSL -import com.mineinabyss.geary.serialization.fileSystem +import okio.FileSystem import okio.Path @GearyDSL class PrefabsDSL( + private val fileSystem: FileSystem, private val loader: PrefabLoader, - private val namespaced: Namespaced + private val namespaced: Namespaced, ) { /** Loads prefab entities from all files inside a [directory][from], into a given [namespace] */ fun from( diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt index fd23e370f..22622feb7 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt @@ -1,21 +1,27 @@ package com.mineinabyss.geary.prefabs import com.mineinabyss.geary.addons.Namespaced +import com.mineinabyss.geary.addons.dsl.Addon import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.addons.dsl.createAddon +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.prefabs.configuration.systems.* import com.mineinabyss.geary.prefabs.systems.createInheritPrefabsOnLoadListener import com.mineinabyss.geary.prefabs.systems.createTrackPrefabsByKeyListener +import com.mineinabyss.geary.serialization.FileSystem +import com.mineinabyss.geary.serialization.SerializableComponents +import com.mineinabyss.geary.serialization.formats.Formats interface PrefabsModule { val manager: PrefabManager val loader: PrefabLoader } -val Prefabs = createAddon("Prefabs", { +val Prefabs get() = createAddon("Prefabs", { + val formats = getAddon(SerializableComponents).formats object : PrefabsModule { override val manager = PrefabManager() - override val loader: PrefabLoader = PrefabLoader() + override val loader: PrefabLoader = PrefabLoader(this@createAddon, formats, logger) } }) { systems { @@ -36,5 +42,5 @@ val Prefabs = createAddon("Prefabs", { } @GearyDSL -fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit) = - setup.install(Prefabs) { PrefabsDSL(loader, this@prefabs).configure() } +fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit): Addon = + setup.install(Prefabs) { PrefabsDSL(setup.geary.getAddon(FileSystem), loader, this@prefabs).configure() } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystem.kt similarity index 83% rename from addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt rename to addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystem.kt index baf5a1a01..dea1b412c 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystemAddon.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/FileSystem.kt @@ -3,7 +3,7 @@ package com.mineinabyss.geary.serialization import com.mineinabyss.geary.addons.dsl.createAddon import okio.FileSystem -val FileSystemAddon = createAddon( +val FileSystem = createAddon( "File System", { error("No FileSystem passed into addon, please use FileSystemAddon()") } ) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index a3189410e..2397471fd 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.addons.dsl import co.touchlab.kermit.Logger import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.modules.Geary +import org.koin.core.Koin import org.koin.core.KoinApplication import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -31,6 +32,7 @@ data class AddonSetup( val configuration: Configuration, val application: KoinApplication, ): KoinComponent { + override fun getKoin(): Koin = application.koin val logger = get().withTag(name) val geary: Geary = Geary(application, logger) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index 1067d8687..dbb999ea1 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -25,6 +25,7 @@ import org.koin.core.Koin import org.koin.core.KoinApplication import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import org.koin.core.qualifier.named import kotlin.reflect.KClass /** diff --git a/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt b/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt index d6e5832b6..63bc700bc 100644 --- a/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt +++ b/geary-test/src/main/kotlin/com/mineinabyss/geary/test/GearyTest.kt @@ -2,10 +2,7 @@ package com.mineinabyss.geary.test import com.mineinabyss.geary.engine.Engine import com.mineinabyss.geary.engine.archetypes.ArchetypeProvider -import com.mineinabyss.geary.modules.Geary -import com.mineinabyss.geary.modules.TestEngineModule -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.modules.get +import com.mineinabyss.geary.modules.* import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -26,10 +23,8 @@ abstract class GearyTest : Geary { startEngine() } - fun startEngine() { - println(setupGeary().start().application.koin.get()) - println(setupGeary().start().application.koin.get()) - _application = setupGeary().start().application + fun startEngine(override: UninitializedGearyModule? = null) { + _application = (override ?: setupGeary()).start().application } @AfterAll @@ -38,9 +33,9 @@ abstract class GearyTest : Geary { } /** Recreates the engine. */ - fun resetEngine() { + fun resetEngine(override: UninitializedGearyModule? = null) { clearEngine() - startEngine() + startEngine(override) } companion object { From 0cd483c086a5fa2897dc0081ed243440997fbbea Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 16 Oct 2024 16:53:40 -0400 Subject: [PATCH 4/9] chore: Bump gradlew --- gradlew | 44 +++++++++++++++++++++++++++++++------------- gradlew.bat | 37 +++++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 29 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 1b6c78733..f5feea6d6 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +217,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..9d21a2183 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 5fde1d07d56e232cc58b7527025ec874137bfddc Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 17 Oct 2024 22:34:10 -0400 Subject: [PATCH 5/9] refactor: Finish fixing tests for addons --- .../mineinabyss/geary/actions/ActionGroup.kt | 16 ++-- .../geary/actions/actions/EnsureAction.kt | 11 +-- .../actions/event_binds/EntityObservers.kt | 26 +++---- .../geary/actions/expressions/Expression.kt | 8 +- .../actions/expressions/FunctionExpression.kt | 2 +- .../actions/ConfigEntityObserversTests.kt | 2 +- .../geary/actions/ExpressionDecodingTest.kt | 25 ++++--- .../mineinabyss/geary/autoscan/AutoScanner.kt | 49 ++++-------- .../geary/autoscan/AutoScannerDSL.kt | 74 +++++++++---------- addons/geary-prefabs/build.gradle.kts | 8 +- .../mineinabyss/geary/prefabs/PrefabsDSL.kt | 9 ++- .../geary/prefabs/PrefabsModule.kt | 56 +++++++------- .../components/CopyToInstances.kt | 7 +- .../systems/CopyToInstancesSystem.kt | 6 +- .../systems/ParseChildOnPrefab.kt | 4 +- .../systems/ParseInstancesOnPrefab.kt | 4 +- .../configuration/systems/ParseReEmitEvent.kt | 7 +- .../systems/ParseRelationOnPrefab.kt | 4 +- .../systems/ParseRelationWithDataSystem.kt | 6 +- .../geary/prefabs/helpers/InheritPrefabs.kt | 7 +- .../PrefabByReferenceSerializer.kt | 33 --------- .../prefabs/systems/InheritPrefabsOnLoad.kt | 3 +- .../systems/TrackPrefabsByKeySystem.kt | 11 +-- .../geary/prefabs/CopyToInstancesTest.kt | 28 +++---- .../GearyEntityComponentIdSerializerTest.kt | 25 +++---- .../mineinabyss/geary/prefabs/PrefabTests.kt | 19 ++--- .../serialization/SerializableComponents.kt | 38 ++++++++-- .../serializers/GearyEntitySerializer.kt | 4 +- .../serializers/SerializableComponentId.kt | 9 ++- .../geary/serialization/formats/YamlFormat.kt | 29 ++++---- .../mineinabyss/geary/uuid/UUIDTracking.kt | 2 +- .../geary/uuid/systems/TrackUuidOnAdd.kt | 1 + .../geary/uuid/systems/UnTrackUuidOnRemove.kt | 1 + .../geary/addons/dsl/GearyAddon.kt | 3 +- .../geary/modules/ArchetypeEngineModule.kt | 1 + .../com/mineinabyss/geary/modules/Geary.kt | 11 ++- .../mineinabyss/geary/modules/GearyModule.kt | 4 +- .../mineinabyss/geary/modules/GearySetup.kt | 3 +- .../geary/modules/MutableAddons.kt | 17 ++++- 39 files changed, 275 insertions(+), 298 deletions(-) delete mode 100644 addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/serializers/PrefabByReferenceSerializer.kt diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt index 9a801b8ef..b9982965d 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/ActionGroup.kt @@ -5,12 +5,18 @@ import com.mineinabyss.geary.actions.actions.EnsureAction import com.mineinabyss.geary.actions.event_binds.* import com.mineinabyss.geary.actions.expressions.Expression import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.getWorld import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.SerializedComponents import kotlinx.serialization.ContextualSerializer +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.modules.SerializersModule class ActionEntry( val action: Action, @@ -21,8 +27,7 @@ class ActionEntry( val environment: Map>?, ) -@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") -@Serializable(with = ContextualSerializer::class) +@Serializable(with = ActionGroup.Serializer::class) class ActionGroup( val actions: List, ) : Action { @@ -58,9 +63,7 @@ class ActionGroup( context.register(entry.register, returned) } - class Serializer( - val world: Geary - ) : InnerSerializer, ActionGroup>( + class Serializer : InnerSerializer, ActionGroup>( serialName = "geary:action_group", inner = ListSerializer( PolymorphicListAsMapSerializer.ofComponents( @@ -68,7 +71,7 @@ class ActionGroup( customKeys = mapOf( "when" to { ActionWhen.serializer() }, "register" to { ActionRegister.serializer() }, - "onFail" to { ContextualSerializer(ActionOnFail::class) }, + "onFail" to { ActionOnFail.Serializer() }, "loop" to { ActionLoop.serializer() } ) ) @@ -76,6 +79,7 @@ class ActionGroup( ), inverseTransform = { TODO() }, transform = { + val world = serializersModule.getWorld() val actions = it.mapNotNull { components -> var action: Action? = null var condition: List? = null diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt index a0e597019..5702a0acf 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/actions/EnsureAction.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.actions.actions import com.mineinabyss.geary.actions.* import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.getWorld import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.SerializedComponents @@ -10,9 +11,7 @@ import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -//@Serializable(with = EnsureAction.Serializer::class) -@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") -@Serializable(with = ContextualSerializer::class) +@Serializable(with = EnsureAction.Serializer::class) class EnsureAction( world: Geary, val conditions: SerializedComponents, @@ -43,12 +42,10 @@ class EnsureAction( return true } - class Serializer( - val world: Geary - ) : InnerSerializer( + class Serializer : InnerSerializer( serialName = "geary:ensure", inner = PolymorphicListAsMapSerializer.ofComponents(), inverseTransform = { it.conditions }, - transform = { EnsureAction(world, it) } + transform = { EnsureAction(serializersModule.getWorld(), it) } ) } diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt index e86491787..3a0e54a48 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/event_binds/EntityObservers.kt @@ -3,6 +3,7 @@ package com.mineinabyss.geary.actions.event_binds import com.mineinabyss.geary.actions.ActionGroup import com.mineinabyss.geary.actions.actions.EnsureAction import com.mineinabyss.geary.actions.expressions.Expression +import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.serializers.InnerSerializer import com.mineinabyss.geary.serialization.serializers.SerializableComponentId @@ -15,18 +16,15 @@ import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlin.jvm.JvmInline -@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") -@Serializable(with = ContextualSerializer::class) +@Serializable(with = EntityObservers.Serializer::class) class EntityObservers( val observers: List, ) { - class Serializer( - world: Geary - ) : InnerSerializer, EntityObservers>( + class Serializer : InnerSerializer, EntityObservers>( serialName = "geary:observe", inner = MapSerializer( - SerializableComponentId.serializer(), - ActionGroup.Serializer(world) + ContextualSerializer(ComponentId::class), + ActionGroup.Serializer() ), inverseTransform = { TODO() }, transform = { @@ -54,12 +52,11 @@ class ActionWhen(val conditions: List) { @Serializable value class ActionRegister(val register: String) +@Serializable(with = ActionOnFail.Serializer::class) class ActionOnFail(val action: ActionGroup) { - class Serializer( - world: Geary - ) : InnerSerializer( + class Serializer : InnerSerializer( serialName = "geary:on_fail", - inner = ActionGroup.Serializer(world), + inner = ActionGroup.Serializer(), inverseTransform = ActionOnFail::action, transform = { ActionOnFail(it) } ) @@ -69,12 +66,11 @@ class ActionOnFail(val action: ActionGroup) { @Serializable value class ActionLoop(val expression: String) +@Serializable(with = ActionEnvironment.Serializer::class) class ActionEnvironment(val environment: Map>) { - class Serializer( - world: Geary, - ) : InnerSerializer>, ActionEnvironment>( + object Serializer : InnerSerializer>, ActionEnvironment>( serialName = "geary:with", - inner = MapSerializer(String.serializer(), Expression.Serializer(world, ContextualSerializer(Any::class))), + inner = MapSerializer(String.serializer(), Expression.Serializer(ContextualSerializer(Any::class))), inverseTransform = ActionEnvironment::environment, transform = { ActionEnvironment(it) } ) diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt index 497edf976..9c558cd76 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/Expression.kt @@ -2,6 +2,7 @@ package com.mineinabyss.geary.actions.expressions import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.getWorld import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -13,6 +14,7 @@ import kotlinx.serialization.encoding.decodeStructure import kotlinx.serialization.modules.SerializersModule import kotlin.math.min +@Serializable(with = Expression.Serializer::class) sealed interface Expression { fun evaluate(context: ActionGroupContext): T data class Fixed( @@ -74,13 +76,11 @@ sealed interface Expression { // TODO kaml handles contextual completely different form Json, can we somehow allow both? Otherwise // kaml also has broken contextual serializer support that we need to work around :( - class Serializer( - val world: Geary, - val serializer: KSerializer - ) : KSerializer> { + class Serializer(val serializer: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor = ContextualSerializer(Any::class).descriptor override fun deserialize(decoder: Decoder): Expression { + val world = decoder.serializersModule.getWorld() // Try reading string value, if serial type isn't string, this fails runCatching { decoder.decodeStructure(String.serializer().descriptor) { diff --git a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt index 37270064a..c67db85c6 100644 --- a/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt +++ b/addons/geary-actions/src/commonMain/kotlin/com/mineinabyss/geary/actions/expressions/FunctionExpression.kt @@ -16,7 +16,7 @@ interface FunctionExpression { module: SerializersModule, ): FunctionExpressionWithInput<*, *> { val serializableComponents = world.getAddon(SerializableComponents) - val compClass = ComponentIdSerializer(world).getComponent(name, module) + val compClass = ComponentIdSerializer(serializableComponents.serializers, world).getComponent(name, module) val serializer = serializableComponents.serializers.getSerializerFor(compClass) ?: error("No serializer found for component $name") val expr = diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt index abbbd2a5b..8ce303691 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ConfigEntityObserversTests.kt @@ -52,7 +52,7 @@ class ConfigEntityObserversTests : GearyTest() { """.trimIndent() val format = YamlFormat(getAddon(SerializableComponents).serializers.module) - val entity = format.decodeFromString(GearyEntitySerializer(this), entityDef) + val entity = format.decodeFromString(GearyEntitySerializer(), entityDef) val printed = mutableListOf() observeWithData().exec { printed += event.string } diff --git a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt index abbd24f4b..581c6572d 100644 --- a/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt +++ b/addons/geary-actions/src/jvmTest/kotlin/com/mineinabyss/geary/actions/ExpressionDecodingTest.kt @@ -1,12 +1,12 @@ package com.mineinabyss.geary.actions -import com.charleskorn.kaml.Yaml import com.mineinabyss.geary.actions.expressions.Expression import com.mineinabyss.geary.actions.expressions.FunctionExpression import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.serialization.SerializableComponents import com.mineinabyss.geary.serialization.formats.YamlFormat import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.geary.test.GearyTest @@ -24,6 +24,16 @@ class ExpressionDecodingTest : GearyTest() { val regular: String, ) + override fun setupGeary() = geary(TestEngineModule) { + serialization { + components { + component(TestFunction.serializer()) + } + format("yml", ::YamlFormat) + } + } + + val format get() = getAddon(SerializableComponents).formats["yml"] as YamlFormat // @org.junit.jupiter.api.Test // fun `should correctly decode json`() { // val input = """ @@ -49,7 +59,7 @@ class ExpressionDecodingTest : GearyTest() { "regular": "{{ asdf }}" } """.trimIndent() - Yaml.default.decodeFromString(TestData.serializer(), input) shouldBe TestData( + format.decodeFromString(TestData.serializer(), input) shouldBe TestData( name = Expression.Fixed("variable"), age = Expression.Variable("test"), regular = "{{ asdf }}" @@ -66,17 +76,8 @@ class ExpressionDecodingTest : GearyTest() { @Test fun shouldCorrectlyParseExpressionFunctions() { - resetEngine(geary(TestEngineModule) { - serialization { - components { - component(TestFunction.serializer()) - } - format("yml", ::YamlFormat) - } - }) - val input = "'{{ entity.geary:testFunction{ string: test } }}'" - val expr = Yaml.default.decodeFromString(Expression.Serializer(this, String.serializer()), input) + val expr = format.decodeFromString(Expression.Serializer(String.serializer()), input) expr.evaluate(ActionGroupContext(entity = entity())) shouldBe "test" } } 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 58dd748d1..a06aea036 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 @@ -1,43 +1,24 @@ package com.mineinabyss.geary.autoscan import co.touchlab.kermit.Severity -import com.mineinabyss.geary.addons.GearyPhase -import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.addons.dsl.createAddon import kotlin.reflect.KClass import kotlin.reflect.KFunction -val autoScanner by DI.observe() - -interface AutoScanner { - val scannedComponents: MutableSet> - val scannedSystems: MutableSet> - - fun installSystems() - - companion object Addon : GearyAddonWithDefault { - override fun default() = object : AutoScanner { - private val logger get() = geary.logger - override val scannedComponents = mutableSetOf>() - override val scannedSystems = mutableSetOf>() - - override fun installSystems() { - scannedSystems.asSequence() - .onEach { it.call(geary) } - .map { it.name } - .let { - if (logger.config.minSeverity <= Severity.Verbose) - logger.i("Autoscan loaded singleton systems: ${it.joinToString()}") - else logger.i("Autoscan loaded ${it.count()} singleton systems") - } +val AutoScanAddon = createAddon("Auto Scan", { AutoScanner() }) { + systems { + configuration.scannedSystems.asSequence() + .onEach { it.call(geary) } + .map { it.name } + .let { + if (geary.logger.config.minSeverity <= Severity.Verbose) + geary.logger.i("Autoscan loaded singleton systems: ${it.joinToString()}") + else geary.logger.i("Autoscan loaded ${it.count()} singleton systems") } - } - - override fun AutoScanner.install() { - geary.pipeline.runOnOrAfter(GearyPhase.INIT_SYSTEMS) { - installSystems() - } - } } } + +class AutoScanner( + val scannedComponents: MutableSet> = mutableSetOf(), + val scannedSystems: MutableSet> = mutableSetOf(), +) diff --git a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt index 73e820617..e8799e60d 100644 --- a/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt +++ b/addons/geary-autoscan/src/main/kotlin/com/mineinabyss/geary/autoscan/AutoScannerDSL.kt @@ -3,10 +3,10 @@ package com.mineinabyss.geary.autoscan import co.touchlab.kermit.Severity import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.datatypes.Component +import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.modules.GearyModule import com.mineinabyss.geary.modules.GearySetup -import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.serialization.dsl.serialization +import com.mineinabyss.geary.serialization.serialization import kotlinx.serialization.* import kotlinx.serialization.modules.polymorphic import org.reflections.Reflections @@ -22,17 +22,16 @@ import kotlin.reflect.typeOf fun GearySetup.autoscan( classLoader: ClassLoader, vararg limitToPackages: String, - configure: AutoScannerDSL.() -> Unit -) = - install(AutoScanner).also { AutoScannerDSL(classLoader, limitToPackages.toList()).configure() } + configure: AutoScannerDSL.() -> Unit, +) = install(AutoScanAddon) { AutoScannerDSL(this@autoscan, this, classLoader, limitToPackages.toList()).configure() } @GearyDSL class AutoScannerDSL( + private val setup: GearySetup, + private val autoScanner: AutoScanner, private val classLoader: ClassLoader, - private val limitTo: List -) { - private val logger get() = geary.logger - + private val limitTo: List, +) : Geary by setup.geary { private val reflections: Reflections by lazy { Reflections( ConfigurationBuilder() @@ -65,19 +64,16 @@ class AutoScannerDSL( .map { it.kotlin } .filter { !it.hasAnnotation() } .toList() - - geary { - serialization { - components { - scanned.forEach { scannedComponent -> - runCatching { component(scannedComponent) } - .onFailure { - when { - geary.logger.config.minSeverity <= Severity.Verbose -> geary.logger.w("Failed to register component ${scannedComponent.simpleName}\n${it.stackTraceToString()}") - else -> geary.logger.w("Failed to register component ${scannedComponent.simpleName} ${it::class.simpleName}: ${it.message}") - } + setup.serialization { + components { + scanned.forEach { scannedComponent -> + runCatching { component(scannedComponent) } + .onFailure { + when { + logger.config.minSeverity <= Severity.Verbose -> logger.w("Failed to register component ${scannedComponent.simpleName}\n${it.stackTraceToString()}") + else -> logger.w("Failed to register component ${scannedComponent.simpleName} ${it::class.simpleName}: ${it.message}") } - } + } } } } @@ -109,26 +105,24 @@ class AutoScannerDSL( /** Registers a polymorphic serializer for this [kClass], scanning for any subclasses. */ @OptIn(InternalSerializationApi::class) fun subClassesOf(kClass: KClass) { - geary { - serialization { - application { - polymorphic(kClass) { - val scanned = this@AutoScannerDSL.reflections - .get(Scanners.SubTypes.of(kClass.java).asClass>(this@AutoScannerDSL.classLoader)) - .asSequence() - .map { it.kotlin } - .filter { !it.hasAnnotation() } - .filterIsInstance>() - .toList() - - scanned.forEach { scannedClass -> - runCatching { subclass(scannedClass, scannedClass.serializer()) } - .onFailure { this@AutoScannerDSL.logger.w("Failed to load subclass ${scannedClass.simpleName} of ${kClass.simpleName}") } - } - if (geary.logger.config.minSeverity <= Severity.Verbose) - geary.logger.i("Autoscan found subclasses for ${kClass.simpleName}: ${scanned.joinToString { it.simpleName!! }}") - else geary.logger.i("Autoscan found ${scanned.size} subclasses for ${kClass.simpleName}") + setup.serialization { + module { + polymorphic(kClass) { + val scanned = this@AutoScannerDSL.reflections + .get(Scanners.SubTypes.of(kClass.java).asClass>(this@AutoScannerDSL.classLoader)) + .asSequence() + .map { it.kotlin } + .filter { !it.hasAnnotation() } + .filterIsInstance>() + .toList() + + scanned.forEach { scannedClass -> + runCatching { subclass(scannedClass, scannedClass.serializer()) } + .onFailure { this@AutoScannerDSL.logger.w("Failed to load subclass ${scannedClass.simpleName} of ${kClass.simpleName}") } } + if (logger.config.minSeverity <= Severity.Verbose) + logger.i("Autoscan found subclasses for ${kClass.simpleName}: ${scanned.joinToString { it.simpleName!! }}") + else logger.i("Autoscan found ${scanned.size} subclasses for ${kClass.simpleName}") } } } diff --git a/addons/geary-prefabs/build.gradle.kts b/addons/geary-prefabs/build.gradle.kts index 445493c2e..944ed4c51 100644 --- a/addons/geary-prefabs/build.gradle.kts +++ b/addons/geary-prefabs/build.gradle.kts @@ -13,19 +13,15 @@ kotlin { } } - val commonTest by getting { + val jvmTest by getting { dependencies { implementation(kotlin("test")) + implementation(project(":geary-test")) implementation(idofrontLibs.kotlinx.coroutines.test) implementation(idofrontLibs.kotest.assertions) implementation(idofrontLibs.kotest.property) implementation(project(":geary-core")) implementation(project(":geary-serialization")) - } - } - - val jvmTest by getting { - dependencies { implementation(idofrontLibs.junit.jupiter) } } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt index 0021d5a7b..06c323d16 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt @@ -7,22 +7,23 @@ import okio.Path @GearyDSL class PrefabsDSL( + private val prefabsBuilder: PrefabsBuilder, private val fileSystem: FileSystem, - private val loader: PrefabLoader, private val namespaced: Namespaced, ) { + /** Loads prefab entities from all files inside a [directory][from], into a given [namespace] */ fun from( vararg from: Path, ) { - loader.addSource(PrefabPath(namespaced.namespace) { from.asSequence() }) + prefabsBuilder.paths.add(PrefabPath(namespaced.namespace) { from.asSequence() }) } fun fromRecursive(folder: Path) { - loader.addSource(PrefabPath(namespaced.namespace) { + PrefabPath(namespaced.namespace) { fileSystem .listRecursively(folder, true) .filter { it.name.contains('.') } - }) + } } } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt index 22622feb7..b2ec52289 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt @@ -4,43 +4,49 @@ import com.mineinabyss.geary.addons.Namespaced import com.mineinabyss.geary.addons.dsl.Addon import com.mineinabyss.geary.addons.dsl.GearyDSL import com.mineinabyss.geary.addons.dsl.createAddon -import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.prefabs.configuration.systems.* import com.mineinabyss.geary.prefabs.systems.createInheritPrefabsOnLoadListener import com.mineinabyss.geary.prefabs.systems.createTrackPrefabsByKeyListener import com.mineinabyss.geary.serialization.FileSystem import com.mineinabyss.geary.serialization.SerializableComponents -import com.mineinabyss.geary.serialization.formats.Formats interface PrefabsModule { val manager: PrefabManager val loader: PrefabLoader } -val Prefabs get() = createAddon("Prefabs", { - val formats = getAddon(SerializableComponents).formats - object : PrefabsModule { - override val manager = PrefabManager() - override val loader: PrefabLoader = PrefabLoader(this@createAddon, formats, logger) - } -}) { - systems { - createInheritPrefabsOnLoadListener() - createParseChildOnPrefabListener() - createParseChildrenOnPrefabListener() - createParseInstancesOnPrefabListener() - createParseRelationOnPrefabListener() - createParseRelationWithDataListener() - createTrackPrefabsByKeyListener() - createCopyToInstancesSystem() - reEmitEvent() - } +class PrefabsBuilder { + val paths: MutableList = mutableListOf() +} - entities { - configuration.loader.loadOrUpdatePrefabs() +val Prefabs + get() = createAddon("Prefabs", { + PrefabsBuilder() + }) { + val formats = geary.getAddon(SerializableComponents).formats + val module = object : PrefabsModule { + override val manager = PrefabManager() + override val loader: PrefabLoader = PrefabLoader(geary, formats, logger) + } + + systems { + createInheritPrefabsOnLoadListener() + createParseChildOnPrefabListener() + createParseChildrenOnPrefabListener() + createParseInstancesOnPrefabListener() + createParseRelationOnPrefabListener() + createParseRelationWithDataListener() + createTrackPrefabsByKeyListener() + createCopyToInstancesSystem() + reEmitEvent() + } + + entities { + module.loader.loadOrUpdatePrefabs() + } + module } -} @GearyDSL -fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit): Addon = - setup.install(Prefabs) { PrefabsDSL(setup.geary.getAddon(FileSystem), loader, this@prefabs).configure() } +fun Namespaced.prefabs(configure: PrefabsDSL.() -> Unit): Addon = + setup.install(Prefabs) { PrefabsDSL(this, setup.geary.getConfiguration(FileSystem), this@prefabs).configure() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/CopyToInstances.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/CopyToInstances.kt index e5e7d49f5..0c6092b2a 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/CopyToInstances.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/components/CopyToInstances.kt @@ -2,9 +2,11 @@ package com.mineinabyss.geary.prefabs.configuration.components import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.Entity -import com.mineinabyss.geary.serialization.serializableComponents +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.SerializableComponents import com.mineinabyss.geary.serialization.serializers.SerializedComponents import com.mineinabyss.geary.serialization.setAllPersisting +import kotlinx.serialization.Contextual import kotlinx.serialization.Polymorphic import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -21,6 +23,7 @@ import kotlinx.serialization.Serializable data class CopyToInstances( private val temporary: SerializedComponents? = null, private val persisting: SerializedComponents? = null, + private val world: @Contextual Geary, ) { @Serializable private data class DeepCopy( @@ -28,7 +31,7 @@ data class CopyToInstances( val persisting: List<@Polymorphic Component>? ) - val formats get() = serializableComponents.formats + val formats get() = world.getAddon(SerializableComponents).formats // This is the safest and cleanest way to deep-copy, even if a little performance intense. private val serializedComponents by lazy { 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 96efff8a8..03147aef7 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,12 +1,12 @@ package com.mineinabyss.geary.prefabs.configuration.systems -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observeWithData import com.mineinabyss.geary.observers.events.OnExtend import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances -import com.mineinabyss.geary.systems.builders.observeWithData fun Geary.createCopyToInstancesSystem() = observeWithData() .exec { - val copy = event.baseEntity.get() ?: return@exec + val copy = event.baseEntity.toGeary().get() ?: return@exec copy.decodeComponentsTo(entity) } 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 01c6fa460..135888b25 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 @@ -4,12 +4,12 @@ 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.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet 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.builders.observe import com.mineinabyss.geary.systems.query.query diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt index 8039925b7..5349edcb9 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseInstancesOnPrefab.kt @@ -2,13 +2,13 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.components.relations.NoInherit import com.mineinabyss.geary.helpers.entity -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.prefabs.PrefabKey import com.mineinabyss.geary.prefabs.configuration.components.InheritPrefabs import com.mineinabyss.geary.prefabs.configuration.components.InstancesOnPrefab import com.mineinabyss.geary.prefabs.configuration.components.Prefab -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query fun Geary.createParseInstancesOnPrefabListener() = observe() diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt index f05325c7b..7bcabb644 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/configuration/systems/ParseReEmitEvent.kt @@ -1,12 +1,11 @@ package com.mineinabyss.geary.prefabs.configuration.systems -import com.mineinabyss.geary.helpers.toGeary -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observeWithData import com.mineinabyss.geary.prefabs.configuration.components.ReEmitEvent -import com.mineinabyss.geary.systems.builders.observeWithData fun Geary.reEmitEvent() = observeWithData().exec { - entity.getRelationsByKind(event.findByRelationKind.id).forEach { relation -> + entity.getRelationsByKind(event.findByRelationKind).forEach { relation -> val entity = relation.target.toGeary() if (entity.exists()) entity.emit(event = event.dataComponentId, data = event.data) } 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 eb80bd3a5..d4ed99faa 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,10 +1,10 @@ package com.mineinabyss.geary.prefabs.configuration.systems import com.mineinabyss.geary.helpers.componentId -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query fun Geary.createParseRelationOnPrefabListener() = observe() 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 e97cf4003..9e2d438a1 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,9 +1,9 @@ package com.mineinabyss.geary.prefabs.configuration.systems -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.systems.accessors.RelationWithData -import com.mineinabyss.geary.systems.builders.observe import com.mineinabyss.geary.systems.query.query fun Geary.createParseRelationWithDataListener() = observe() @@ -13,6 +13,6 @@ fun Geary.createParseRelationWithDataListener() = observe() 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) + if (targetData != null) entity.set(targetData, relationWithData.target) entity.remove>() } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt index 85988c2a7..94f8d96fb 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt @@ -15,9 +15,10 @@ fun Entity.inheritPrefabsIfNeeded(instances: Set = setOf()) { val add = get() ?: return remove() add.from.mapNotNull { key -> - key.toEntityOrNull().also { - if (it == null) geary.logger.w("Prefab ${get()} could not inherit prefab $key, it does not exist") - } + TODO() +// key.toEntityOrNull().also { +// if (it == null) geary.logger.w("Prefab ${get()} could not inherit prefab $key, it does not exist") +// } }.forEach { parent -> parent.inheritPrefabsIfNeeded(instances + this) extend(parent) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/serializers/PrefabByReferenceSerializer.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/serializers/PrefabByReferenceSerializer.kt deleted file mode 100644 index e4c4271c1..000000000 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/serializers/PrefabByReferenceSerializer.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.mineinabyss.geary.prefabs.serializers - -import com.mineinabyss.geary.datatypes.Entity -import com.mineinabyss.geary.prefabs.PrefabKey -import com.mineinabyss.geary.prefabs.PrefabManager -import com.mineinabyss.geary.prefabs.prefabs -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder - -/** - * Allows us to serialize entity types to a reference to ones actually registered in the system. - * This is used to load the static entity type when we decode components from an in-game entity. - */ -@Deprecated("This will not work properly until ktx.serialization fully supports inline classes") -class PrefabByReferenceSerializer : KSerializer { - private val prefabManager: PrefabManager get() = prefabs.manager - - override val descriptor = SerialDescriptor("geary:prefab", PrefabKey.serializer().descriptor) - - override fun deserialize(decoder: Decoder): Entity { - val prefabKey = decoder.decodeSerializableValue(PrefabKey.serializer()) - return (prefabManager[prefabKey] ?: error("Error deserializing, $prefabKey is not a registered prefab")) - } - - override fun serialize(encoder: Encoder, value: Entity) { - encoder.encodeSerializableValue( - PrefabKey.serializer(), - value.get() ?: error("Could not encode prefab entity without a prefab key component") - ) - } -} 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 2208c4dba..2cb13deb2 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,10 +1,9 @@ package com.mineinabyss.geary.prefabs.systems import com.mineinabyss.geary.modules.Geary -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.prefabs.events.PrefabLoaded import com.mineinabyss.geary.prefabs.helpers.inheritPrefabsIfNeeded -import com.mineinabyss.geary.systems.builders.observe fun Geary.createInheritPrefabsOnLoadListener() = observe() .exec { entity.inheritPrefabsIfNeeded() } 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 b787647e8..f88c59a86 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,15 +1,16 @@ package com.mineinabyss.geary.prefabs.systems import com.mineinabyss.geary.components.relations.NoInherit -import com.mineinabyss.geary.modules.GearyModule +import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe +import com.mineinabyss.geary.observers.Observer import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.prefabs.PrefabKey -import com.mineinabyss.geary.prefabs.prefabs -import com.mineinabyss.geary.systems.builders.observe +import com.mineinabyss.geary.prefabs.Prefabs import com.mineinabyss.geary.systems.query.query -fun Geary.createTrackPrefabsByKeyListener() = observe() +fun Geary.createTrackPrefabsByKeyListener(): Observer = observe() .involving(query()).exec { (key) -> - prefabs.manager.registerPrefab(key, entity) + getAddon(Prefabs).manager.registerPrefab(key, entity) entity.addRelation() } diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt index 03237c469..128c982b4 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/CopyToInstancesTest.kt @@ -7,27 +7,22 @@ import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.configuration.components.CopyToInstances import com.mineinabyss.geary.serialization.getAllPersisting import com.mineinabyss.geary.serialization.serialization -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.test.GearyTest import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe import kotlinx.serialization.builtins.serializer -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class CopyToInstancesTest { - @BeforeEach - fun createEngine() { - DI.clear() - geary(TestEngineModule) { - install(Prefabs) - - serialization { - components { - component(String.serializer()) - component(Int.serializer()) - } +class CopyToInstancesTest : GearyTest() { + override fun setupGeary() = geary(TestEngineModule) { + serialization { + components { + component(String.serializer()) + component(Int.serializer()) } - }.start() + } + + install(Prefabs) } @Test @@ -37,7 +32,8 @@ class CopyToInstancesTest { set( CopyToInstances( temporary = listOf(42), - persisting = listOf("Hello world") + persisting = listOf("Hello world"), + world = world, ) ) addRelation() diff --git a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt index a1db2449f..e3572dcc9 100644 --- a/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt +++ b/addons/geary-prefabs/src/jvmTest/kotlin/com/mineinabyss/geary/prefabs/GearyEntityComponentIdSerializerTest.kt @@ -2,13 +2,13 @@ package com.mineinabyss.geary.prefabs import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.geary.serialization.dsl.serialization +import com.mineinabyss.geary.serialization.SerializableComponents import com.mineinabyss.geary.serialization.formats.YamlFormat -import com.mineinabyss.geary.serialization.serializableComponents +import com.mineinabyss.geary.serialization.serialization import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer.Companion.provideConfig -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.collections.shouldContainExactly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -17,18 +17,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class GearyEntityComponentIdSerializerTest { - - init { - DI.clear() - geary(TestEngineModule) { - serialization { - components { - component(A.serializer()) - } +class GearyEntityComponentIdSerializerTest : GearyTest() { + override fun setupGeary() = geary(TestEngineModule) { + serialization { + components { + component(A.serializer()) } } - geary.pipeline.runStartupTasks() } @Serializable @@ -38,7 +33,7 @@ class GearyEntityComponentIdSerializerTest { @Test fun `GearyEntitySerializer should deserialize to entity correctly`() { // arrange - val format = YamlFormat(serializableComponents.serializers.module) + val format = YamlFormat(getAddon(SerializableComponents).serializers.module) val file = """ thing.a: {} @@ -46,7 +41,7 @@ class GearyEntityComponentIdSerializerTest { // act val entity = - format.decodeFromString(GearyEntitySerializer, file, overrideSerializersModule = SerializersModule { + format.decodeFromString(GearyEntitySerializer(), file, overrideSerializersModule = SerializersModule { provideConfig(PolymorphicListAsMapSerializer.Config(namespaces = listOf("test"))) }) 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 5be3c5c10..1cd216123 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 @@ -3,21 +3,18 @@ package com.mineinabyss.geary.prefabs import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.TestEngineModule import com.mineinabyss.geary.modules.geary -import com.mineinabyss.idofront.di.DI +import com.mineinabyss.geary.serialization.SerializableComponents +import com.mineinabyss.geary.test.GearyTest import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class PrefabTests { +class PrefabTests : GearyTest() { private val testKey = PrefabKey.of("test:1") - @BeforeEach - fun createEngine() { - DI.clear() - geary(TestEngineModule) { - install(Prefabs) - } + override fun setupGeary() = geary(TestEngineModule) { + install(SerializableComponents) + install(Prefabs) } @Test @@ -29,7 +26,7 @@ class PrefabTests { val instance = entity { extend(prefab) } // assert - testKey.toEntity() shouldBe prefab + entityOfOrNull(testKey) shouldBe prefab instance.get().shouldBeNull() } @@ -39,6 +36,6 @@ class PrefabTests { val prefab = entity { set(testKey) } // assert - testKey.toEntityOrNull() shouldBe prefab + entityOfOrNull(testKey) shouldBe prefab } } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt index 412218d9b..1ac4ff663 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt @@ -12,13 +12,16 @@ import com.mineinabyss.geary.serialization.dsl.builders.ComponentSerializersBuil import com.mineinabyss.geary.serialization.dsl.builders.FormatsBuilder import com.mineinabyss.geary.serialization.formats.Format import com.mineinabyss.geary.serialization.formats.Formats +import com.mineinabyss.geary.serialization.serializers.ComponentIdSerializer import com.mineinabyss.geary.serialization.serializers.GearyEntitySerializer +import com.mineinabyss.geary.serialization.serializers.SerializableComponentId +import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.SerializersModuleBuilder -import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.modules.* import kotlinx.serialization.serializerOrNull import kotlin.reflect.KClass @@ -27,7 +30,7 @@ data class SerializableComponentsBuilder( val serializers: ComponentSerializersBuilder = ComponentSerializersBuilder(), val formats: FormatsBuilder = FormatsBuilder(), var overridePersists: ComponentId? = null, - var addGearyEntitySerializer: Boolean = false, + var addGearyEntitySerializer: Boolean = true, ) { /** Adds a [SerializersModule] for polymorphic serialization of [Component]s within the ECS. */ inline fun components(crossinline init: PolymorphicModuleBuilder.() -> Unit) { @@ -74,12 +77,18 @@ data class SerializableComponentsBuilder( } fun build(): SerializableComponentsModule { + module { + println("Adding Geary to serializers") + contextual(GearyWorldProvider(world)) + contextual(ComponentIdSerializer(serializers.build(), world)) + } + val serializers = serializers.build() if (addGearyEntitySerializer) { - components { component(GearyEntitySerializer(world)) } + components { component(GearyEntitySerializer()) } } return SerializableComponentsModule( - serializers = serializers.build(), - formats = formats.build(serializers.build()), + serializers = serializers, + formats = formats.build(serializers), persists = overridePersists ?: world.componentId() ) } @@ -99,3 +108,16 @@ val SerializableComponents = createAddon Unit) = install(SerializableComponents, configure) + +fun SerializersModule.getWorld(): Geary = (getContextual(Geary::class) as GearyWorldProvider).world + +class GearyWorldProvider(val world: Geary): KSerializer { + override val descriptor: SerialDescriptor = ContextualSerializer(Any::class).descriptor + + override fun deserialize(decoder: Decoder): Geary { + return world + } + + override fun serialize(encoder: Encoder, value: Geary) { + } +} diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt index 526c56711..e597acba4 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/GearyEntitySerializer.kt @@ -4,6 +4,7 @@ import com.mineinabyss.geary.datatypes.GearyEntity import com.mineinabyss.geary.helpers.entity import com.mineinabyss.geary.modules.Geary import com.mineinabyss.geary.serialization.getAllPersisting +import com.mineinabyss.geary.serialization.getWorld import kotlinx.serialization.Contextual import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -14,11 +15,12 @@ import kotlinx.serialization.encoding.Encoder //TODO register contextual serializer for world typealias SerializableGearyEntity = @Contextual GearyEntity -class GearyEntitySerializer(val world: Geary) : KSerializer { +class GearyEntitySerializer() : KSerializer { private val componentSerializer = PolymorphicListAsMapSerializer.ofComponents() override val descriptor = SerialDescriptor("geary:entity", componentSerializer.descriptor) override fun deserialize(decoder: Decoder): GearyEntity { + val world = decoder.serializersModule.getWorld() return world.entity { setAll(componentSerializer.deserialize(decoder)) } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt index e76d6a93e..2e258ca27 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/SerializableComponentId.kt @@ -4,6 +4,7 @@ import com.mineinabyss.geary.datatypes.Component import com.mineinabyss.geary.datatypes.ComponentId import com.mineinabyss.geary.helpers.componentId import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.serialization.ComponentSerializers import com.mineinabyss.geary.serialization.SerializableComponents import kotlinx.serialization.Contextual import kotlinx.serialization.KSerializer @@ -16,8 +17,10 @@ import kotlin.reflect.KClass typealias SerializableComponentId = @Contextual ComponentId -class ComponentIdSerializer(val world: Geary) : KSerializer { - val serializers = world.getAddon(SerializableComponents).serializers +class ComponentIdSerializer( + val componentSerializers: ComponentSerializers, + val world: Geary +) : KSerializer { override val descriptor = PrimitiveSerialDescriptor("EventComponent", PrimitiveKind.STRING) private val polymorphicListAsMapSerializer = PolymorphicListAsMapSerializer.ofComponents() @@ -34,6 +37,6 @@ class ComponentIdSerializer(val world: Geary) : KSerializer decodeFromFile( 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 cb0a8fbf6..69651f947 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 @@ -4,7 +4,7 @@ import com.mineinabyss.geary.addons.dsl.createAddon import com.mineinabyss.geary.uuid.systems.trackUUIDOnAdd import com.mineinabyss.geary.uuid.systems.untrackUuidOnRemove -val UUIDTracking = createAddon("UUID Tracking", ::SimpleUUID2GearyMap) { +val UUIDTracking = createAddon("UUID Tracking", { SimpleUUID2GearyMap() }) { onStart { trackUUIDOnAdd(configuration) untrackUuidOnRemove(configuration) 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 cbc647381..89d293c56 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 @@ -1,6 +1,7 @@ package com.mineinabyss.geary.uuid.systems import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnSet import com.mineinabyss.geary.systems.query.query import com.mineinabyss.geary.uuid.UUID2GearyMap 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 765d9c037..927e1d0ec 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,6 +1,7 @@ package com.mineinabyss.geary.uuid.systems import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.observe import com.mineinabyss.geary.observers.events.OnRemove import com.mineinabyss.geary.systems.query.query import com.mineinabyss.geary.uuid.UUID2GearyMap diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index 2397471fd..3400f3fc6 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -79,9 +79,10 @@ data class AddonSetup( } } +@JvmName("createAddon0") fun createAddon( name: String, - init: AddonSetup.() -> Unit = {}, + init: AddonSetup.() -> Unit, ): Addon = Addon(name, { }) { init(AddonSetup(name, it, application)) } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt index 84c47af3c..211d6066c 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/ArchetypeEngineModule.kt @@ -82,6 +82,7 @@ fun ArchetypeEngineModule( } withOptions { bind() } + singleOf(::MutableAddons) }, properties = mapOf( "tickDuration" to tickDuration, "reuseIDsAfterRemoval" to reuseIDsAfterRemoval, diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index dbb999ea1..68b0460b4 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -25,7 +25,6 @@ import org.koin.core.Koin import org.koin.core.KoinApplication import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import org.koin.core.qualifier.named import kotlin.reflect.KClass /** @@ -42,6 +41,7 @@ interface Geary : KoinComponent { abstract val application: KoinApplication open val logger: Logger get() = application.koin.get() override fun getKoin(): Koin = application.koin + // By default, we always get the latest instance of deps, the Impl class gets them once for user // access where the engine isn't expected to be reloaded (ex. like it might be in tests) val eventRunner: EventRunner get() = get() @@ -55,12 +55,11 @@ interface Geary : KoinComponent { val componentProvider: ComponentProvider get() = get() val records: ArrayTypeMap get() = get() val engine: GearyEngine get() = get() + val addons: MutableAddons get() = get() - // @JvmName("getAddon1") - fun , Inst> getAddon(addon: T): Inst = TODO() + fun , Inst> getAddon(addon: T): Inst = addons.getInstance(addon) - // @JvmName("getAddon2") -// fun , Inst> getAddon(addon: T?): Inst? = TODO() + fun , Conf> getConfiguration(addon: T): Conf = addons.getConfig(addon) // Queries @@ -158,5 +157,5 @@ inline fun Geary.observeWithData(): ObserverWithData { } } -inline fun Geary.findEntities(init: MutableFamily.Selector.And.() -> Unit)= +inline fun Geary.findEntities(init: MutableFamily.Selector.And.() -> Unit) = findEntities(family(init)) 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 287816eb8..ad70fa5c9 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 @@ -26,9 +26,9 @@ data class UninitializedGearyModule( inline fun configure(configure: GearySetup.() -> Unit) = setup.configure() fun start(): Geary { - setup.addons.initAll(setup) - initializer.start() val world = Geary(setup.application) + world.addons.initAll(setup) + initializer.start() world.pipeline.runStartupTasks() // TODO keep pipeline separate, it shouldnt be used after init return world } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt index fea42dfea..00a21116a 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt @@ -12,12 +12,11 @@ import org.koin.core.KoinApplication class GearySetup( val application: KoinApplication, ) { - val addons = MutableAddons() val logger = application.koin.get() val geary = Geary(application) inline fun , Conf> install(addon: T, configure: Conf.() -> Unit = {}): T { - addons.getOrPut(geary, addon).apply { config.configure() } + geary.addons.getOrPut(geary, addon).apply { config.configure() } return addon } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt index dc1607bc8..cd020a5f0 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt @@ -7,17 +7,28 @@ class MutableAddons { @PublishedApi internal val addons = mutableMapOf>() + internal val instances = mutableMapOf() + internal val addonsOrder = mutableListOf>() + + fun getInstance(addon: Addon<*, I>): I { + return instances[addon.name] as? I ?: error("Instance for addon ${addon.name} not found") + } fun getOrPut(world: Geary, addon: Addon, customConfiguration: (() -> T)? = null): AddonToConfig { return addons.getOrPut(addon.name) { AddonToConfig(addon, customConfiguration?.invoke() ?: addon.defaultConfiguration(world)) + .also { addonsOrder.add(it) } } as AddonToConfig } + fun getConfig(addon: Addon): T { + return addons[addon.name]?.config as? T ?: error("Config for addon ${addon.name} not found") + } + fun initAll(setup: GearySetup) { - addons.forEach { (name, addon) -> - val geary = Geary(setup.application, setup.logger.withTag(name)) - (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) + addonsOrder.forEach { addon -> + val geary = Geary(setup.application, setup.logger.withTag(addon.addon.name)) + instances[addon.addon.name] = (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) } } } From 4de49731531dd88a7db4e5f8abda1ee44a1ef525 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Fri, 18 Oct 2024 21:39:49 -0400 Subject: [PATCH 6/9] fix: entityOf and entityOfOrNull swapped logic feat: getAddonOrNull --- .../kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt | 6 +++--- .../kotlin/com/mineinabyss/geary/modules/Geary.kt | 5 ++++- .../kotlin/com/mineinabyss/geary/modules/MutableAddons.kt | 4 ++-- .../com/mineinabyss/geary/systems/query/QueriedEntity.kt | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt index 02e003998..962645525 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabKey.kt @@ -33,7 +33,7 @@ data class PrefabKey private constructor(val namespace: String, val key: String) } } -fun Geary.entityOfOrNull(key: PrefabKey): Entity = entityOf(key) - ?: error("Requested non null prefab entity for $this, but it does not exist.") +fun Geary.entityOfOrNull(key: PrefabKey?): Entity? = key?.let { getAddon(Prefabs).manager[key] } -fun Geary.entityOf(key: PrefabKey): Entity? = getAddon(Prefabs).manager[key] +fun Geary.entityOf(key: PrefabKey): Entity = entityOfOrNull(key) + ?: error("Requested non null prefab entity for $this, but it does not exist.") diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index 68b0460b4..597dd69db 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -57,7 +57,10 @@ interface Geary : KoinComponent { val engine: GearyEngine get() = get() val addons: MutableAddons get() = get() - fun , Inst> getAddon(addon: T): Inst = addons.getInstance(addon) + fun , Inst> getAddon(addon: T): Inst = + addons.getInstance(addon) ?: error("Instance for addon ${addon.name} not found") + + fun , Inst> getAddonOrNull(addon: T?): Inst? = addon?.let { addons.getInstance(addon) } fun , Conf> getConfiguration(addon: T): Conf = addons.getConfig(addon) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt index cd020a5f0..5d03d1a02 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt @@ -10,8 +10,8 @@ class MutableAddons { internal val instances = mutableMapOf() internal val addonsOrder = mutableListOf>() - fun getInstance(addon: Addon<*, I>): I { - return instances[addon.name] as? I ?: error("Instance for addon ${addon.name} not found") + fun getInstance(addon: Addon<*, I>): I? { + return instances[addon.name] as? I } fun getOrPut(world: Geary, addon: Addon, customConfiguration: (() -> T)? = null): AddonToConfig { 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 2a69ff9e3..49066540b 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 @@ -16,7 +16,7 @@ import com.mineinabyss.geary.systems.accessors.FamilyMatching open class QueriedEntity( final override val world: Geary, final override val cacheAccessors: Boolean, -) : AccessorOperations() { +) : AccessorOperations(), Geary by world { @PublishedApi @UnsafeAccessors internal var archetype = world.get().rootArchetype From 0852f6778c736c105f0142ddc18b7c0510141a25 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Tue, 22 Oct 2024 14:15:43 -0400 Subject: [PATCH 7/9] chore: Add some helper methods for caching queries --- .../kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt | 2 +- .../kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt | 2 +- .../kotlin/com/mineinabyss/geary/modules/Geary.kt | 6 ++++++ .../com/mineinabyss/geary/systems/query/CachedQuery.kt | 6 ++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt index 6a80d3222..88a1af7b8 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabLoader.kt @@ -27,7 +27,7 @@ class PrefabLoader( ) { private val readFiles = mutableListOf() - private val needsInherit = world.cache(NeedsInherit(world)) + private val needsInherit = world.cache(::NeedsInherit) fun addSource(path: PrefabPath) { readFiles.add(path) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index 3400f3fc6..dcff0c6b7 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -22,7 +22,7 @@ data class Addon( val defaultConfiguration: Geary.() -> Configuration, val onInstall: Geary.(Configuration) -> Instance, ) { - operator fun invoke(customConfiguration: Geary.() -> Configuration): Addon { + fun withConfig(customConfiguration: Geary.() -> Configuration): Addon { return copy(defaultConfiguration = customConfiguration) } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index 597dd69db..3b1ca1fe3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -76,6 +76,12 @@ interface Geary : KoinComponent { return queryManager.trackQuery(query) } + fun cache( + create: (Geary) -> T, + ): CachedQuery { + return cache(create(this)) + } + fun system( query: T, ): SystemBuilder { diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt index 6175673b6..399d83173 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/systems/query/CachedQuery.kt @@ -188,6 +188,12 @@ class CachedQuery internal constructor(val query: T) { forEach { entities.add(Entity(it.unsafeEntity, world)) } return entities } + + fun count(): Int { + var count = 0 + forEach { count++ } + return count + } } inline fun List>.execOnFinish(run: (data: R, entity: GearyEntity) -> Unit) { From 2e5408eaa9d2640eb4456bd522b953c9d9c4bc4a Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 01:16:13 -0400 Subject: [PATCH 8/9] chore: Some helper functions for addons --- .../kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt | 6 ++++++ .../kotlin/com/mineinabyss/geary/modules/Geary.kt | 4 ++++ .../kotlin/com/mineinabyss/geary/modules/MutableAddons.kt | 8 ++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt index dcff0c6b7..ff69b2ed3 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/addons/dsl/GearyAddon.kt @@ -3,10 +3,12 @@ package com.mineinabyss.geary.addons.dsl import co.touchlab.kermit.Logger import com.mineinabyss.geary.addons.GearyPhase import com.mineinabyss.geary.modules.Geary +import com.mineinabyss.geary.modules.GearySetup import org.koin.core.Koin import org.koin.core.KoinApplication import org.koin.core.component.KoinComponent import org.koin.core.component.get +import org.koin.core.module.Module import kotlin.jvm.JvmName interface GearyAddonWithDefault : GearyAddon { @@ -77,6 +79,10 @@ data class AddonSetup( fun on(phase: GearyPhase, run: () -> Unit) { geary.pipeline.runOnOrAfter(phase, run) } + + fun inject(vararg modules: Module) { + application.modules(*modules) + } } @JvmName("createAddon0") diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt index 3b1ca1fe3..6bd507636 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/Geary.kt @@ -98,6 +98,10 @@ interface Geary : KoinComponent { return SystemBuilder(defaultName, query, pipeline) } + fun loadAddon(addon: Addon<*, *>) { + addons.init(addons.getOrPut(this, addon), setup = GearySetup(application)) + } + fun relationOf(kind: KClass<*>, target: KClass<*>): Relation = Relation.of(componentId(kind), componentId(target)) diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt index 5d03d1a02..5c38dedfa 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/MutableAddons.kt @@ -25,10 +25,14 @@ class MutableAddons { return addons[addon.name]?.config as? T ?: error("Config for addon ${addon.name} not found") } + fun init(addon: AddonToConfig<*>, setup: GearySetup) { + val geary = Geary(setup.application, setup.logger.withTag(addon.addon.name)) + instances[addon.addon.name] = (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) + } + fun initAll(setup: GearySetup) { addonsOrder.forEach { addon -> - val geary = Geary(setup.application, setup.logger.withTag(addon.addon.name)) - instances[addon.addon.name] = (addon.addon.onInstall as Geary.(Any?) -> Any).invoke(geary, addon.config) + init(addon, setup) } } } From 0a713874f3416da4ddd2f99fdad399fae4525624 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Fri, 25 Oct 2024 13:33:38 -0400 Subject: [PATCH 9/9] fix: Actually load prefabs defined by prefab addon --- .../com/mineinabyss/geary/prefabs/PrefabsDSL.kt | 4 ++-- .../com/mineinabyss/geary/prefabs/PrefabsModule.kt | 6 +++--- .../geary/prefabs/helpers/InheritPrefabs.kt | 14 +++++++------- .../geary/serialization/SerializableComponents.kt | 1 - .../geary/serialization/SerializersByMap.kt | 5 +++-- .../serializers/PolymorphicListAsMapSerializer.kt | 5 +++-- .../com/mineinabyss/geary/modules/GearyModule.kt | 1 - .../com/mineinabyss/geary/modules/GearySetup.kt | 8 ++++++++ 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt index 06c323d16..2be146961 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsDSL.kt @@ -20,10 +20,10 @@ class PrefabsDSL( } fun fromRecursive(folder: Path) { - PrefabPath(namespaced.namespace) { + prefabsBuilder.paths.add(PrefabPath(namespaced.namespace) { fileSystem .listRecursively(folder, true) .filter { it.name.contains('.') } - } + }) } } diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt index b2ec52289..c88de9b56 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/PrefabsModule.kt @@ -20,15 +20,15 @@ class PrefabsBuilder { } val Prefabs - get() = createAddon("Prefabs", { - PrefabsBuilder() - }) { + get() = createAddon("Prefabs", { PrefabsBuilder() }) { val formats = geary.getAddon(SerializableComponents).formats val module = object : PrefabsModule { override val manager = PrefabManager() override val loader: PrefabLoader = PrefabLoader(geary, formats, logger) } + configuration.paths.forEach { module.loader.addSource(it) } + systems { createInheritPrefabsOnLoadListener() createParseChildOnPrefabListener() diff --git a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt index 94f8d96fb..89616dc6f 100644 --- a/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt +++ b/addons/geary-prefabs/src/commonMain/kotlin/com/mineinabyss/geary/prefabs/helpers/InheritPrefabs.kt @@ -4,23 +4,23 @@ import com.mineinabyss.geary.datatypes.Entity import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.prefabs.PrefabKey import com.mineinabyss.geary.prefabs.configuration.components.InheritPrefabs +import com.mineinabyss.geary.prefabs.entityOfOrNull /** * Adds prefabs to this entity from an [InheritPrefabs] component. Will make sure parents have their prefabs * added from this component before trying to add it */ -fun Entity.inheritPrefabsIfNeeded(instances: Set = setOf()) { - if (this in instances) +fun Entity.inheritPrefabsIfNeeded(instances: Set = setOf()): Unit = with(world) { + if (this@inheritPrefabsIfNeeded in instances) error("Circular dependency found while loading prefabs for ${get()}, chain was: $instances") val add = get() ?: return remove() add.from.mapNotNull { key -> - TODO() -// key.toEntityOrNull().also { -// if (it == null) geary.logger.w("Prefab ${get()} could not inherit prefab $key, it does not exist") -// } + entityOfOrNull(key).also { + if (it == null) logger.w("Prefab ${get()} could not inherit prefab $key, it does not exist") + } }.forEach { parent -> - parent.inheritPrefabsIfNeeded(instances + this) + parent.inheritPrefabsIfNeeded(instances + this@inheritPrefabsIfNeeded) extend(parent) } } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt index 1ac4ff663..e5670c472 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializableComponents.kt @@ -78,7 +78,6 @@ data class SerializableComponentsBuilder( fun build(): SerializableComponentsModule { module { - println("Adding Geary to serializers") contextual(GearyWorldProvider(world)) contextual(ComponentIdSerializer(serializers.build(), world)) } diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializersByMap.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializersByMap.kt index 71a8dc92b..0fd5f8c94 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializersByMap.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/SerializersByMap.kt @@ -19,13 +19,14 @@ class SerializersByMap( //TODO allow this to work for all registered classes, not just components override fun getClassFor(serialName: String, namespaces: List): KClass { + val defaultNamespaces = (namespaces + "geary").toSet() val parsedKey = serialName.fromCamelCaseToSnakeCase() return (if (parsedKey.hasNamespace()) serialName2Component[parsedKey] - else namespaces.firstNotNullOfOrNull { namespace -> + else defaultNamespaces.firstNotNullOfOrNull { namespace -> serialName2Component["$namespace:$parsedKey"] }) - ?: error("$parsedKey is not a component registered in any of the namespaces: $namespaces") + ?: error("$parsedKey is not a component registered in any of the namespaces: $defaultNamespaces") } override fun getSerializerFor( diff --git a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt index 51444a3f2..04b29d159 100644 --- a/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt +++ b/addons/geary-serialization/src/commonMain/kotlin/com/mineinabyss/geary/serialization/serializers/PolymorphicListAsMapSerializer.kt @@ -101,12 +101,13 @@ open class PolymorphicListAsMapSerializer( if (key.startsWith("kotlin.")) { return@runCatching serializersModule.getPolymorphic(polymorphicSerializer.baseClass, key) as KSerializer } + val defaultNamespaces = namespaces.plus("geary").toSet() val parsedKey = "${config.prefix}$key".fromCamelCaseToSnakeCase() return@runCatching (if (parsedKey.hasNamespace()) serializersModule.getPolymorphic(polymorphicSerializer.baseClass, parsedKey) - else namespaces.firstNotNullOfOrNull { namespace -> + else defaultNamespaces.firstNotNullOfOrNull { namespace -> serializersModule.getPolymorphic(polymorphicSerializer.baseClass, "$namespace:$parsedKey") - } ?: error("No serializer found for $parsedKey in any of the namespaces $namespaces")) + } ?: error("No serializer found for $parsedKey in any of the namespaces $defaultNamespaces")) as? KSerializer ?: error("Serializer for $parsedKey is not a component serializer") } 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 ad70fa5c9..0a989b90c 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 @@ -29,7 +29,6 @@ data class UninitializedGearyModule( val world = Geary(setup.application) world.addons.initAll(setup) initializer.start() - world.pipeline.runStartupTasks() // TODO keep pipeline separate, it shouldnt be used after init return world } } diff --git a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt index 00a21116a..9391ea404 100644 --- a/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt +++ b/geary-core/src/commonMain/kotlin/com/mineinabyss/geary/modules/GearySetup.kt @@ -3,6 +3,8 @@ package com.mineinabyss.geary.modules import co.touchlab.kermit.Logger import com.mineinabyss.geary.addons.Namespaced import com.mineinabyss.geary.addons.dsl.Addon +import com.mineinabyss.geary.addons.dsl.AddonSetup +import com.mineinabyss.geary.addons.dsl.createAddon import org.koin.core.KoinApplication /** @@ -20,6 +22,12 @@ class GearySetup( return addon } + inline fun install(name: String, crossinline init: AddonSetup.() -> Unit) { + install(createAddon(name) { + init() + }) + } + fun namespace(namespace: String, configure: Namespaced.() -> Unit) { Namespaced(namespace, this).configure() }