Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mob hotswaps, bugfixes for async reads #23

Merged
merged 14 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@ package com.mineinabyss.geary.papermc.bridge

import com.mineinabyss.geary.addons.GearyPhase
import com.mineinabyss.geary.addons.dsl.GearyAddonWithDefault
import com.mineinabyss.geary.autoscan.autoScanner
import com.mineinabyss.geary.autoscan.autoscan
import com.mineinabyss.geary.helpers.component
import com.mineinabyss.geary.modules.geary
import com.mineinabyss.geary.papermc.GearyPaperConfigModule
import com.mineinabyss.geary.papermc.bridge.actions.*
import com.mineinabyss.geary.papermc.bridge.actions.DoDamageSystem
import com.mineinabyss.geary.papermc.bridge.actions.DoKnockbackSystem
import com.mineinabyss.geary.papermc.bridge.actions.DoSpawnSystem
import com.mineinabyss.geary.papermc.bridge.actions.ExplosionSystem
import com.mineinabyss.geary.papermc.bridge.conditions.*
import com.mineinabyss.geary.papermc.bridge.conditions.location.BlockConditionChecker
import com.mineinabyss.geary.papermc.bridge.conditions.location.HeightConditionChecker
import com.mineinabyss.geary.papermc.bridge.conditions.location.LightConditionChecker
import com.mineinabyss.geary.papermc.bridge.conditions.location.TimeConditionChecker
import com.mineinabyss.geary.papermc.bridge.config.OnEvent
import com.mineinabyss.geary.papermc.bridge.config.parsers.CreateDefaultSkills
import com.mineinabyss.geary.papermc.bridge.config.parsers.ParseSkills
import com.mineinabyss.geary.papermc.bridge.events.entities.*
import com.mineinabyss.geary.papermc.bridge.events.items.ItemBreakBridge
import com.mineinabyss.geary.papermc.bridge.events.items.ItemConsumeBridge
import com.mineinabyss.geary.papermc.bridge.events.items.ItemInteractBridge
import com.mineinabyss.geary.papermc.bridge.mythicmobs.DoMMSkillSystem
import com.mineinabyss.geary.papermc.bridge.mythicmobs.RunMMSkillSystem
import com.mineinabyss.geary.papermc.bridge.readers.ReadLocationSystem
import com.mineinabyss.geary.papermc.bridge.readers.ReadTargetBlockSystem
import com.mineinabyss.geary.papermc.bridge.targetselectors.NearbyEntitiesSelector
import com.mineinabyss.geary.papermc.gearyPaper
import com.mineinabyss.geary.papermc.tracking.items.components.SetItem
import com.mineinabyss.idofront.di.DI
import com.mineinabyss.idofront.plugin.listeners

Expand All @@ -33,6 +38,10 @@ class PaperBridge {
autoscan(this::class.java.classLoader, "com.mineinabyss.geary.papermc.bridge") {
systems()
}

component<SetItem>().apply {
addRelation<OnEvent, OnSpawn>()
}
}
geary.pipeline.addSystems(
BlockConditionChecker(),
Expand All @@ -47,7 +56,7 @@ class PaperBridge {
CreateDefaultSkills(),
DoSpawnSystem(),
ReadTargetBlockSystem(),
DoMMSkillSystem(),
RunMMSkillSystem(),
DoDamageSystem(),
NearbyEntitiesSelector(),
ReadLocationSystem(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ data class Explosion(
val setFire: Boolean = false,
val breakBlocks: Boolean = false,
val fuseTicks: Int = 0,
val location: Input<@Contextual Location>,
val at: Input<@Contextual Location>,
)

class ExplosionSystem : GearyListener() {
private val Pointers.explosion by get<Explosion>().on(source)

override fun Pointers.handle() {
val location = explosion.location.get(this)
val location = explosion.at.get(this)
if (explosion.fuseTicks <= 0) location.createExplosion(
explosion.power, explosion.setFire, explosion.breakBlocks
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mineinabyss.geary.papermc.bridge.conditions

import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.datatypes.GearyEntity
import com.mineinabyss.geary.events.CheckingListener
import com.mineinabyss.geary.systems.accessors.Pointers
import com.mineinabyss.idofront.serialization.DurationSerializer
Expand All @@ -15,18 +16,32 @@ import kotlin.time.Duration
class Cooldown(
val length: @Serializable(with = DurationSerializer::class) Duration,
val displayName: @Serializable(with = MiniMessageSerializer::class) Component? = null,
)
) {

class CooldownStarted(val time: Long)
companion object {
fun isComplete(entity: GearyEntity, id: GearyEntity): Boolean {
val cooldownStarted = entity.getRelation<CooldownStarted>(id)
val isComplete =
cooldownStarted == null || System.currentTimeMillis() - cooldownStarted.time >= cooldownStarted.cooldown.length.inWholeMilliseconds
if (isComplete) entity.removeRelation<CooldownStarted>(id)
return isComplete
}

fun start(entity: GearyEntity, source: GearyEntity, cooldown: Cooldown) {
entity.setRelation(CooldownStarted(System.currentTimeMillis(), cooldown), source)
}
}
}

class CooldownStarted(val time: Long, val cooldown: Cooldown)

class CooldownChecker : CheckingListener() {
private val Pointers.cooldownDefinition by get<Cooldown>().on(source)

@OptIn(UnsafeAccessors::class)
override fun Pointers.check(): Boolean {
val cooldown = target.entity.getRelation<CooldownStarted>(source!!.entity)
if (cooldown == null || System.currentTimeMillis() - cooldown.time >= cooldownDefinition.length.inWholeMilliseconds) {
target.entity.setRelation(CooldownStarted(System.currentTimeMillis()), source!!.entity)
if (Cooldown.isComplete(target.entity, source!!.entity)) {
Cooldown.start(target.entity, source!!.entity, cooldownDefinition)
return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ object EventHelpers {
}
}

fun runSkill(target: GearyEntity, skill: Skill) {
temporaryEntity { event ->
runSkill(target, event, skill)
}
}

fun <T : Any> runSkill(
target: GearyEntity,
initiator: GearyEntity = target,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.mineinabyss.geary.papermc.bridge.mythicmobs

import com.google.common.collect.Lists
import com.mineinabyss.geary.serialization.serializers.InnerSerializer
import com.mineinabyss.geary.systems.GearyListener
import com.mineinabyss.geary.systems.accessors.Pointers
import com.mineinabyss.idofront.typealiases.BukkitEntity
import io.lumine.mythic.api.adapters.AbstractEntity
import io.lumine.mythic.bukkit.BukkitAdapter
import io.lumine.mythic.bukkit.MythicBukkit
import io.lumine.mythic.core.skills.SkillMetadataImpl
import io.lumine.mythic.core.skills.SkillTriggers
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlin.jvm.optionals.getOrNull

@Serializable(with= RunMythicMobsSkills.Serializer::class)
class RunMythicMobsSkills(
val keys: List<String>,
) {
class Serializer : InnerSerializer<List<String>, RunMythicMobsSkills>(
serialName = "geary:run_mythic_skills",
inner = ListSerializer(String.serializer()),
inverseTransform = { it.keys },
transform = ::RunMythicMobsSkills
)
}

class RunMMSkillSystem: GearyListener() {
val Pointers.bukkit by get<BukkitEntity>().on(target)
val Pointers.skill by get<RunMythicMobsSkills>().on(source)

override fun Pointers.handle() {
skill.keys.forEach {
val line = "[ - $it ]"
val entity = BukkitAdapter.adapt(bukkit)
val caster = MythicBukkit.inst().skillManager.getCaster(entity)
val skill = MythicBukkit.inst().skillManager.getSkill(line).getOrNull()
val meta = SkillMetadataImpl(
SkillTriggers.API,
caster,
entity,
entity.location,
Lists.newArrayList(*arrayOf<AbstractEntity>(entity)),
null,
1.0f
)
skill?.execute(meta)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.toDuration

private val INTERVAL = 3.ticks
private val INTERVAL = 1.ticks

@AutoScan
class CooldownDisplaySystem : RepeatingSystem(interval = INTERVAL) {
Expand All @@ -27,22 +27,34 @@ class CooldownDisplaySystem : RepeatingSystem(interval = INTERVAL) {

@OptIn(UnsafeAccessors::class)
override fun Pointer.tick() {
// val mainHand = player.inventory.toGeary()?.itemInMainHand ?: return
val cooldowns = cooldowns.mapNotNull { relation ->
//TODO this should be a separate system once we have system priorities set up
val validCooldowns = cooldowns.filter { relation ->
fun removeCooldown() = entity.removeRelation<CooldownStarted>(relation.target)
if (!relation.target.exists()) {
removeCooldown()
return@filter false
}
val cooldown = relation.data.cooldown
val timeLeft = cooldown.length - (System.currentTimeMillis() - relation.data.time)
.toDuration(DurationUnit.MILLISECONDS)
if (timeLeft.isNegative()) {
removeCooldown()
return@filter false
}
true
}

val cooldownsWithDisplay = validCooldowns.mapNotNull { relation ->
val cooldown = relation.target.get<Cooldown>() ?: return@mapNotNull null
if (cooldown.displayName == null) return@mapNotNull null

val timeLeft = cooldown.length - (System.currentTimeMillis() - relation.data.time)
.toDuration(DurationUnit.MILLISECONDS)
if (timeLeft.isNegative()) {
//TODO separate system should be in charge of this
entity.removeRelation<CooldownStarted>(relation.target)
return@mapNotNull null
}
CooldownInfo(cooldown.displayName, timeLeft, cooldown.length)
}

player.sendActionBar(
Component.join(JoinConfiguration.commas(true), cooldowns.map { cooldown ->
Component.join(JoinConfiguration.commas(true), cooldownsWithDisplay.map { cooldown ->
val squaresLeft =
if (cooldown.timeLeft < INTERVAL) 0 else (cooldown.timeLeft / cooldown.length * displayLength).roundToInt()

Expand Down
1 change: 1 addition & 0 deletions geary-papermc-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id(libs.plugins.mia.kotlin.jvm.get().pluginId)
id(libs.plugins.mia.papermc.get().pluginId)
id(libs.plugins.mia.nms.get().pluginId)
id(libs.plugins.mia.publication.get().pluginId)
alias(libs.plugins.kotlinx.serialization)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package com.mineinabyss.geary.papermc

import co.touchlab.kermit.Severity
import com.charleskorn.kaml.YamlComment
import com.mineinabyss.geary.modules.geary
import io.papermc.paper.util.TickThread
import kotlinx.serialization.Serializable
import org.bukkit.entity.EntityType
import org.spigotmc.AsyncCatcher

@Serializable
class GearyPaperConfig(
Expand All @@ -16,11 +20,44 @@ class GearyPaperConfig(
val bridgeEvents: Boolean = true,
@YamlComment("If an item has no prefabs encoded, try to find its prefab by matching custom model data.")
val migrateItemCustomModelDataToPrefab: Boolean = true,
val catch: Catching = Catching(),
val mobTypeConversion: MobTypeConversion = MobTypeConversion.IGNORE,
@YamlComment("List of mob types to remove if they are not entities with Geary prefabs (i.e. vanilla entities)")
val removeVanillaMobTypes: Set<EntityType> = emptySet(),
val logLevel: Severity = Severity.Warn,
)

@Serializable
class Catching(
@YamlComment("Whether to throw an error when an entity read operation occurs outside of the server thread.")
val catchAsyncRead: Boolean = false,
val asyncRead: CatchType = CatchType.WARN,
@YamlComment("Whether to throw an error when an entity write operation occurs outside of the server thread.")
val catchAsyncWrite: Boolean = true,
val asyncWrite: CatchType = CatchType.ERROR,
@YamlComment("Whether to throw an error when converting bukkit concepts to geary entities outside of the server thread.")
val catchAsyncEntityConversion: Boolean = false,
val logLevel: Severity = Severity.Warn,
)
val asyncEntityConversion: CatchType = CatchType.WARN,
val asyncRecordsAccess: CatchType = CatchType.WARN,
val asyncArchetypeProviderAccess: CatchType = CatchType.WARN,
) {
companion object{
fun asyncCheck(type: CatchType, message: String) {
when (type) {
CatchType.ERROR -> AsyncCatcher.catchOp(message)
CatchType.WARN -> if (!TickThread.isTickThread()) {
geary.logger.w(message)
IllegalStateException("(Ignoring) $message").printStackTrace()
}
CatchType.IGNORE -> Unit
}
}
}
}

enum class CatchType {
ERROR, IGNORE, WARN
}



enum class MobTypeConversion {
MIGRATE, REMOVE, IGNORE
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.mineinabyss.geary.components.relations.Persists
import com.mineinabyss.geary.datatypes.GearyEntity
import com.mineinabyss.geary.helpers.component
import com.mineinabyss.geary.helpers.toGeary
import com.mineinabyss.geary.modules.geary
import com.mineinabyss.idofront.typealiases.BukkitEntity
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataHolder
Expand All @@ -24,6 +26,8 @@ fun GearyEntity.encodeComponentsTo(pdc: PersistentDataContainer) {
}

fun GearyEntity.encodeComponentsTo(holder: PersistentDataHolder) {
val bukkitHolder = holder as? BukkitEntity
geary.logger.d("Encoding components for bukkit entity $id (${bukkitHolder?.type} ${bukkitHolder?.uniqueId})")
encodeComponentsTo(holder.persistentDataContainer)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.mineinabyss.geary.papermc.plugin.commands.locate
import com.mineinabyss.geary.papermc.plugin.commands.query
import com.mineinabyss.geary.papermc.tracking.entities.gearyMobs
import com.mineinabyss.geary.papermc.tracking.entities.helpers.spawnFromPrefab
import com.mineinabyss.geary.papermc.tracking.entities.systems.updatemobtype.UpdateMob
import com.mineinabyss.geary.papermc.tracking.entities.toGeary
import com.mineinabyss.geary.papermc.tracking.items.cache.PlayerItemCache
import com.mineinabyss.geary.papermc.tracking.items.gearyItems
Expand All @@ -26,11 +27,13 @@ import com.mineinabyss.idofront.commands.extensions.actions.playerAction
import com.mineinabyss.idofront.messaging.error
import com.mineinabyss.idofront.messaging.info
import com.mineinabyss.idofront.messaging.success
import com.mineinabyss.idofront.typealiases.BukkitEntity
import okio.Path.Companion.toOkioPath
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.Plugin
import kotlin.io.path.Path
import kotlin.io.path.nameWithoutExtension
Expand Down Expand Up @@ -124,8 +127,20 @@ internal class GearyCommands : IdofrontCommandExecutor(), TabCompleter {
runCatching { prefabLoader.reload(prefabEntity) }
.onSuccess { sender.success("Reread prefab $prefab") }
.onFailure { sender.error("Failed to reread prefab $prefab:\n${it.message}") }


// Reload entities
geary.queryManager.getEntitiesMatching(family {
hasRelation<InstanceOf?>(prefabEntity)
has<BukkitEntity>()
}).forEach {
UpdateMob.recreateGearyEntity(it.get<BukkitEntity>() ?: return@forEach)
}

// Reload items
geary.queryManager.getEntitiesMatching(family {
hasRelation<InstanceOf?>(prefabEntity)
has<ItemStack>()
}).toSet()
.mapNotNull { it.parent }
.forEach { it.get<Player>()?.inventory?.toGeary()?.forceRefresh(ignoreCached = true) }
Expand Down
Loading
Loading