Major optimisations to minion ticking

This commit is contained in:
TomTom 2023-10-09 19:52:17 +02:00
parent 24f5e29381
commit 00090ce56e
11 changed files with 136 additions and 19 deletions

View File

@ -93,4 +93,8 @@ interface Minion : InventoryHolder {
fun getChestLocationId(): Int
fun removeOpenInventory(inventory: Inventory)
fun isTicking(): Boolean
fun setTicking(ticking: Boolean)
}

View File

@ -25,16 +25,20 @@ abstract class MinionType(private val name: String, private val defaults: InputS
return this.name
}
open fun onToolDirty(minion: Minion) {
}
open fun shouldRun(minion: Minion): Boolean {
return true
}
fun isChunkLoaded(location: Location): Boolean {
return location.world?.isChunkLoaded(location.blockX shr 4, location.blockZ shr 4) ?: return false
fun isTicking(minion: Minion): Boolean {
return minion.isTicking()
}
fun tick(minion: Minion) {
if (!isChunkLoaded(minion.getLocation())) return
if (!minion.isTicking()) return
if (!shouldRun(minion)) return
run(minion)

View File

@ -0,0 +1,8 @@
package com.artillexstudios.axminions.api.minions.utils
data class ChunkPos(var x: Int, var z: Int) {
fun clone(): ChunkPos {
return ChunkPos(x, z)
}
}

View File

@ -14,6 +14,7 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.commands.AxMinionsCommand
import com.artillexstudios.axminions.data.H2DataHandler
import com.artillexstudios.axminions.listeners.ChunkListener
import com.artillexstudios.axminions.listeners.LinkingListener
import com.artillexstudios.axminions.listeners.MinionInventoryListener
import com.artillexstudios.axminions.listeners.MinionPlaceListener
@ -80,6 +81,7 @@ class AxMinionsPlugin : AxPlugin() {
Bukkit.getPluginManager().registerEvents(MinionPlaceListener(), this)
Bukkit.getPluginManager().registerEvents(LinkingListener(), this)
Bukkit.getPluginManager().registerEvents(MinionInventoryListener(), this)
Bukkit.getPluginManager().registerEvents(ChunkListener(), this)
MinionTicker.startTicking()
}

View File

@ -74,7 +74,7 @@ class AxMinionsCommand {
val total = minions.size
minions.fastFor {
if (it.getType().isChunkLoaded(it.getLocation())) {
if (it.getType().isTicking(it)) {
loaded++
}
}

View File

@ -0,0 +1,20 @@
package com.artillexstudios.axminions.listeners
import com.artillexstudios.axminions.minions.Minions
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.world.ChunkLoadEvent
import org.bukkit.event.world.ChunkUnloadEvent
class ChunkListener : Listener {
@EventHandler
fun onChunkLoadEvent(event: ChunkLoadEvent) {
Minions.addTicking(event.chunk)
}
@EventHandler
fun onChunkUnloadEvent(event: ChunkUnloadEvent) {
Minions.removeTicking(event.chunk)
}
}

View File

@ -47,6 +47,7 @@ class MinionPlaceListener : Listener {
}
val minion = Minion(location, event.player.uniqueId, event.player, minionType, 1, ItemStack(Material.AIR), null, Direction.NORTH, 0, 0.0, AxMinionsPlugin.dataHandler.getLocationID(location), 0)
minion.setTicking(true)
AxMinionsPlugin.dataHandler.saveMinion(minion)
event.player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.PLACE_SUCCESS(), Placeholder.unparsed("type", minionType.getName()), Placeholder.unparsed("placed", (placed + 1).toString()), Placeholder.unparsed("max", (maxMinions).toString())))

View File

@ -25,14 +25,12 @@ import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Location
import org.bukkit.OfflinePlayer
import org.bukkit.block.Container
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.EntityType
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.util.EulerAngle
import java.util.UUID
import kotlin.math.roundToInt
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.persistence.PersistentDataType
@ -52,8 +50,8 @@ class Minion(
private var chestLocationId: Int
) : Minion {
private lateinit var entity: PacketArmorStand
private var nextAction = 0
private var range = 0.0
internal var nextAction = 0
internal var range = 0.0
private var dirty = true
private var armTick = 2.0
private var warning: Warning? = null
@ -61,6 +59,8 @@ class Minion(
private val extraData = hashMapOf<String, String>()
private var linkedInventory: Inventory? = null
private val openInventories = mutableListOf<Inventory>()
@Volatile
private var ticking = false
init {
spawn()
@ -95,9 +95,7 @@ class Minion(
override fun tick() {
if (dirty) {
dirty = false
range = type.getDouble("range", level)
val efficiency = 1.0 - (getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1)
nextAction = (type.getLong("speed", level) * efficiency).roundToInt()
type.onToolDirty(this)
}
type.tick(this)
@ -342,6 +340,14 @@ class Minion(
openInventories.remove(inventory)
}
override fun isTicking(): Boolean {
return ticking
}
override fun setTicking(ticking: Boolean) {
this.ticking = ticking
}
override fun getInventory(): Inventory {
return Bukkit.createInventory(this, 9)
}

View File

@ -7,9 +7,12 @@ object MinionTicker {
private var tick = 0L
private inline fun tickAll() {
Minions.getMinions().fastFor { minion ->
minion.tick()
Minions.get().values.forEach { list ->
list.fastFor {
it.tick()
}
}
tick++
}

View File

@ -1,21 +1,81 @@
package com.artillexstudios.axminions.minions
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.utils.ChunkPos
import com.artillexstudios.axminions.utils.fastFor
import java.util.Collections
import java.util.concurrent.ConcurrentLinkedQueue
import org.bukkit.Chunk
object Minions {
private val entities = ConcurrentLinkedQueue<Minion>()
private val mutex = Object()
private val minions = hashMapOf<ChunkPos, ArrayList<Minion>>()
private val mutableChunkPos = ChunkPos(0, 0)
fun addTicking(chunk: Chunk) {
synchronized(mutex) {
mutableChunkPos.x = chunk.x
mutableChunkPos.z = chunk.z
minions[mutableChunkPos]?.fastFor {
it.setTicking(true)
} ?: return
}
}
fun removeTicking(chunk: Chunk) {
synchronized(mutex) {
mutableChunkPos.x = chunk.x
mutableChunkPos.z = chunk.z
val minions = this.minions[mutableChunkPos] ?: return
minions.fastFor {
it.setTicking(false)
}
}
}
fun load(minion: Minion) {
entities.add(minion)
synchronized(mutex) {
mutableChunkPos.x = round(minion.getLocation().x) shr 4
mutableChunkPos.z = round(minion.getLocation().z) shr 4
val pos = minions[mutableChunkPos] ?: arrayListOf()
pos.add(minion)
minions[mutableChunkPos] = pos
}
}
fun remove(minion: Minion) {
entities.remove(minion)
synchronized(mutex) {
mutableChunkPos.x = minion.getLocation().blockX shr 4
mutableChunkPos.z = minion.getLocation().blockZ shr 4
val pos = minions[mutableChunkPos] ?: return
pos.remove(minion)
if (pos.isEmpty()) {
minions.remove(mutableChunkPos)
}
}
}
fun getMinions(): List<Minion> {
return Collections.unmodifiableList(entities.toList())
synchronized(mutex) {
val list = mutableListOf<Minion>()
minions.forEach { (_, value) ->
list.addAll(value)
}
return Collections.unmodifiableList(list)
}
}
internal fun get(): HashMap<ChunkPos, ArrayList<Minion>> {
synchronized(mutex) {
return minions
}
}
private infix fun round(double: Double): Int {
return (double + 0.5).toInt()
}
}

View File

@ -5,7 +5,9 @@ import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.minions.MinionTicker
import kotlin.math.roundToInt
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Item
class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.getResource("minions/collector.yml")!!) {
@ -14,6 +16,13 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
return MinionTicker.getTick() % minion.getNextAction() == 0L
}
override fun onToolDirty(minion: Minion) {
val minionImpl = minion as com.artillexstudios.axminions.minions.Minion
minionImpl.range = getDouble("range", minion.getLevel())
val efficiency = 1.0 - (minion.getTool()?.getEnchantmentLevel(Enchantment.DIG_SPEED)?.div(10.0) ?: 0.1)
minionImpl.nextAction = (getLong("speed", minion.getLevel()) * efficiency).roundToInt()
}
override fun run(minion: Minion) {
minion.resetAnimation()
if (minion.getLinkedChest() == null) {
@ -22,7 +31,7 @@ class CollectorMinionType : MinionType("collector", AxMinionsPlugin.INSTANCE.get
}
val type = minion.getLinkedChest()!!.block.type
if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL && !type.name.lowercase().contains("shulker_box")) {
if (type != Material.CHEST && type != Material.TRAPPED_CHEST && type != Material.BARREL) {
Warnings.NO_CONTAINER.display(minion)
minion.setLinkedChest(null)
return