Skip to content

Commit

Permalink
feat: Config driven passive systems
Browse files Browse the repository at this point in the history
  • Loading branch information
0ffz committed Jul 28, 2024
1 parent 1777992 commit 96284a4
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package com.mineinabyss.geary.actions

import com.mineinabyss.geary.actions.actions.EmitEventAction
import com.mineinabyss.geary.actions.actions.EnsureAction
import com.mineinabyss.geary.actions.event_binds.ActionRegister
import com.mineinabyss.geary.actions.event_binds.ActionWhen
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.Serializable
import kotlinx.serialization.builtins.ListSerializer

class ActionEntry(
val action: Action,
val conditions: List<EnsureAction>?,
val register: String?,
)

@Serializable(with = ActionGroup.Serializer::class)
class ActionGroup(
val actions: List<ActionEntry>,
) {
Expand All @@ -27,4 +37,41 @@ class ActionGroup(
}
}
}

object Serializer : InnerSerializer<List<SerializedComponents>, ActionGroup>(
serialName = "geary:action_group",
inner = ListSerializer(
PolymorphicListAsMapSerializer.ofComponents(
PolymorphicListAsMapSerializer.Config(
customKeys = mapOf(
"when" to ActionWhen.serializer(),
"register" to ActionRegister.serializer()
)
)
)
),
inverseTransform = { TODO() },
transform = {
val actions = it.mapNotNull { components ->
var action: Action? = null
var condition: List<EnsureAction>? = null
var register: String? = null
components.forEach { comp ->
when {
comp is ActionWhen -> condition = comp.conditions
comp is ActionRegister -> register = comp.register
action != null -> geary.logger.w { "Multiple actions defined in one block!" }
else -> action = EmitEventAction.wrapIfNotAction(comp)
}
}
if (action == null) return@mapNotNull null
ActionEntry(
action = action!!,
conditions = condition,
register = register
)
}
ActionGroup(actions)
}
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mineinabyss.geary.actions

import com.mineinabyss.geary.actions.event_binds.bindEntityObservers
import com.mineinabyss.geary.addons.GearyPhase
import com.mineinabyss.geary.actions.event_binds.parsePassive
import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault
import com.mineinabyss.geary.modules.geary

Expand All @@ -12,6 +12,7 @@ class GearyActions {
override fun GearyActions.install() {
geary.run {
bindEntityObservers()
parsePassive()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,28 @@
package com.mineinabyss.geary.actions.event_binds

import com.mineinabyss.geary.actions.Action
import com.mineinabyss.geary.actions.ActionEntry
import com.mineinabyss.geary.actions.actions.EmitEventAction
import com.mineinabyss.geary.actions.ActionGroup
import com.mineinabyss.geary.actions.actions.EnsureAction
import com.mineinabyss.geary.serialization.serializers.InnerSerializer
import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer
import com.mineinabyss.geary.serialization.serializers.SerializableComponentId
import com.mineinabyss.geary.serialization.serializers.SerializedComponents
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlin.jvm.JvmInline

@Serializable(with = EntityObservers.Serializer::class)
class EntityObservers(
val observers: List<EventBind>,
) {
class Serializer : InnerSerializer<Map<SerializableComponentId, List<SerializedComponents>>, EntityObservers>(
class Serializer : InnerSerializer<Map<SerializableComponentId, ActionGroup>, EntityObservers>(
serialName = "geary:observe",
inner = MapSerializer(
SerializableComponentId.serializer(),
ListSerializer(
PolymorphicListAsMapSerializer.ofComponents(
PolymorphicListAsMapSerializer.Config(
customKeys = mapOf(
"when" to ActionWhen.serializer(),
"register" to ActionRegister.serializer()
)
)
)
)
ActionGroup.Serializer
),
inverseTransform = { TODO() },
transform = {
EntityObservers(
it.map { (event, emit) ->
val actions = emit.map { components ->
var action: Action? = null
var condition: List<EnsureAction>? = null
var register: String? = null
components.forEach { comp ->
when {
comp is ActionWhen -> condition = comp.conditions
comp is ActionRegister -> register = comp.register
action != null -> error("Multiple actions defined in one block!")
else -> action = EmitEventAction.wrapIfNotAction(comp)
}
}
ActionEntry(
action = action!!,
conditions = condition,
register = register
)
}
EventBind(event, emit = actions)
it.map { (event, actionGroup) ->
EventBind(event, actionGroup = actionGroup)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.mineinabyss.geary.actions.event_binds

import com.mineinabyss.geary.actions.ActionEntry
import com.mineinabyss.geary.actions.ActionGroup
import com.mineinabyss.geary.serialization.serializers.SerializableComponentId
import com.mineinabyss.geary.serialization.serializers.SerializedComponents

class EventBind(
val event: SerializableComponentId,
val involving: List<SerializableComponentId> = listOf(),
val emit: List<ActionEntry>,
val actionGroup: ActionGroup,
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.mineinabyss.geary.actions.event_binds

import com.mineinabyss.geary.actions.ActionGroup
import com.mineinabyss.geary.actions.ActionGroupContext
import com.mineinabyss.geary.actions.actions.EmitEventAction
import com.mineinabyss.geary.datatypes.EntityType
import com.mineinabyss.geary.modules.GearyModule
import com.mineinabyss.geary.observers.entity.observe
Expand All @@ -14,7 +12,7 @@ fun GearyModule.bindEntityObservers() = observe<OnSet>()
.involving(query<EntityObservers>())
.exec { (observers) ->
observers.observers.forEach { observer ->
val actionGroup = ActionGroup(observer.emit)
val actionGroup = observer.actionGroup
entity.observe(observer.event.id).involving(EntityType(observer.involving.map { it.id })).exec {
val context = ActionGroupContext(entity)
actionGroup.execute(context)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.mineinabyss.geary.actions.event_binds;

import com.mineinabyss.geary.actions.ActionGroup
import com.mineinabyss.geary.actions.ActionGroupContext
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.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
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

@Serializable
class SystemBind(
val match: List<SerializableComponentId>,
val every: @Serializable(with = DurationSerializer::class) Duration = 1.seconds,
val run: ActionGroup,
)

@Serializable(with = Passive.Serializer::class)
class Passive(
val systems: List<SystemBind>,
) {
object Serializer : InnerSerializer<List<SystemBind>, Passive>(
serialName = "geary:passive",
inner = ListSerializer(SystemBind.serializer()),
inverseTransform = Passive::systems,
transform = ::Passive
)
}

fun GearyModule.parsePassive() = observe<OnSet>()
.involving(query<Passive>())
.exec { (passive) ->
passive.systems.forEach { systemBind ->
val systemMatchingId = entity().id
entity.add(systemMatchingId)
system(query {
has(systemMatchingId)
has(systemBind.match.map { it.id })
}).every(systemBind.every).execOnAll {
entities().fastForEach { entity ->
runCatching {
val context = ActionGroupContext(entity)
systemBind.run.execute(context)
}.onFailure { it.printStackTrace() }
}
}
}
entity.remove<Passive>()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.mineinabyss.geary.actions.serializers

import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

internal object DurationSerializer : KSerializer<Duration> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Time", PrimitiveKind.STRING)

override fun serialize(encoder: Encoder, value: Duration) =
encoder.encodeString(value.toString())

override fun deserialize(decoder: Decoder): Duration {
val string = decoder.decodeString()
return Duration.parseOrNull(string) ?: fromString(decoder.decodeString()) ?: error("Not a valid duration: $string")
}

private fun fromString(string: String): Duration? {
val splitAt = string.indexOfFirst { it.isLetter() }.takeIf { it > 0 } ?: string.length
val value = string.take(splitAt).toDouble()
return when (string.drop(splitAt)) {
"ms" -> value.milliseconds
"s" -> value.seconds
"m" -> value.minutes
"h" -> value.hours
"d" -> value.days
"w" -> value.days * 7
"mo" -> value.days * 31
else -> null
}
}
}

0 comments on commit 96284a4

Please sign in to comment.