Minor memory leak fix, try fixing weird ticking bug, new feature

This commit is contained in:
TomTom 2024-01-31 17:08:18 +01:00
parent ebd053e9ee
commit 19eb8f561e
15 changed files with 86 additions and 57 deletions

View File

@ -9,6 +9,7 @@ import com.artillexstudios.axapi.libs.boostedyaml.boostedyaml.settings.updater.U
import com.artillexstudios.axminions.api.AxMinionsAPI import com.artillexstudios.axminions.api.AxMinionsAPI
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.util.Locale
class Config(file: File, stream: InputStream) { class Config(file: File, stream: InputStream) {
companion object { companion object {
@ -42,6 +43,8 @@ class Config(file: File, stream: InputStream) {
@JvmStatic @JvmStatic
fun PULL_FROM_CHEST() = AxMinionsAPI.INSTANCE.getConfig().get("pull-tools-from-chest", false) fun PULL_FROM_CHEST() = AxMinionsAPI.INSTANCE.getConfig().get("pull-tools-from-chest", false)
@JvmStatic @JvmStatic
fun UPGRADE_FAIL() = AxMinionsAPI.INSTANCE.getConfig().get("upgrade-fail", "chat").lowercase(Locale.ENGLISH)
@JvmStatic
fun PLACE_PERMISSION() = AxMinionsAPI.INSTANCE.getConfig().get("place-permissions", false) fun PLACE_PERMISSION() = AxMinionsAPI.INSTANCE.getConfig().get("place-permissions", false)
@JvmStatic @JvmStatic
fun DEBUG(): Boolean { fun DEBUG(): Boolean {

View File

@ -7,7 +7,6 @@ import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axminions.api.AxMinionsAPI import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.utils.Keys import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
import java.io.File import java.io.File
@ -33,12 +32,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
return true return true
} }
fun isTicking(minion: Minion): Boolean {
return minion.isTicking()
}
fun tick(minion: Minion) { fun tick(minion: Minion) {
if (!minion.isTicking()) return
if (!shouldRun(minion)) return if (!shouldRun(minion)) return
minion.resetAnimation() minion.resetAnimation()

View File

@ -1,10 +1,9 @@
package com.artillexstudios.axminions.api.minions.utils package com.artillexstudios.axminions.api.minions.utils
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.utils.fastFor
import org.bukkit.World import org.bukkit.World
data class ChunkPos(val world: World, val x: Int, val z: Int) { data class ChunkPos(val world: World, val x: Int, val z: Int, @Volatile var ticking: Boolean) {
val minions = arrayListOf<Minion>() val minions = arrayListOf<Minion>()
val worldUUID = world.uid val worldUUID = world.uid
@ -19,8 +18,10 @@ data class ChunkPos(val world: World, val x: Int, val z: Int) {
} }
fun setTicking(ticking: Boolean) { fun setTicking(ticking: Boolean) {
minions.fastFor { this.ticking = ticking
it.setTicking(ticking)
minions.forEach {
it.setTicking(true)
} }
} }

View File

@ -1,7 +1,9 @@
package com.artillexstudios.axminions.api.utils package com.artillexstudios.axminions.api.utils
import java.util.WeakHashMap
class CoolDown<T> { class CoolDown<T> {
private val map = HashMap<T, Long>() private val map = WeakHashMap<T, Long>()
fun add(value: T, time: Long) { fun add(value: T, time: Long) {
expire() expire()

View File

@ -123,7 +123,7 @@ allprojects {
compileOnly 'com.intellectualsites.plotsquared:plotsquared-core:7.0.0-rc.4' compileOnly 'com.intellectualsites.plotsquared:plotsquared-core:7.0.0-rc.4'
compileOnly 'com.intellectualsites.plotsquared:plotsquared-bukkit:7.0.0-rc.4' compileOnly 'com.intellectualsites.plotsquared:plotsquared-bukkit:7.0.0-rc.4'
implementation platform('com.intellectualsites.bom:bom-newest:1.35') implementation platform('com.intellectualsites.bom:bom-newest:1.35')
implementation("com.artillexstudios.axapi:axapi:1.4.22") implementation("com.artillexstudios.axapi:axapi:1.4.23")
implementation("net.byteflux:libby-bukkit:1.3.0") implementation("net.byteflux:libby-bukkit:1.3.0")
implementation("com.zaxxer:HikariCP:5.1.0") implementation("com.zaxxer:HikariCP:5.1.0")
implementation("org.bstats:bstats-bukkit:3.0.2") implementation("org.bstats:bstats-bukkit:3.0.2")

View File

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

View File

@ -5,26 +5,27 @@ import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.config.Config import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
import java.util.UUID import java.util.WeakHashMap
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
class LinkingListener : Listener { class LinkingListener : Listener {
companion object { companion object {
val linking = hashMapOf<UUID, Minion>() val linking = WeakHashMap<Player, Minion>()
private val CONTAINERS = listOf(Material.BARREL, Material.CHEST, Material.TRAPPED_CHEST) private val CONTAINERS = listOf(Material.BARREL, Material.CHEST, Material.TRAPPED_CHEST)
} }
@EventHandler @EventHandler
fun onPlayerInteractEvent(event: PlayerInteractEvent) { fun onPlayerInteractEvent(event: PlayerInteractEvent) {
if (event.player.uniqueId !in linking) return
if (event.clickedBlock == null) return if (event.clickedBlock == null) return
if (event.player !in linking) return
if (event.clickedBlock!!.type !in CONTAINERS) return if (event.clickedBlock!!.type !in CONTAINERS) return
if (!AxMinionsPlugin.integrations.getProtectionIntegration().canBuildAt(event.player, event.clickedBlock!!.location)) return if (!AxMinionsPlugin.integrations.getProtectionIntegration().canBuildAt(event.player, event.clickedBlock!!.location)) return
val minion = linking.remove(event.player.uniqueId) ?: return val minion = linking.remove(event.player) ?: return
event.isCancelled = true event.isCancelled = true
if (minion.getLocation() if (minion.getLocation()
.distanceSquared(event.clickedBlock!!.location) > Config.MAX_LINKING_DISTANCE() * Config.MAX_LINKING_DISTANCE() .distanceSquared(event.clickedBlock!!.location) > Config.MAX_LINKING_DISTANCE() * Config.MAX_LINKING_DISTANCE()

View File

@ -3,6 +3,7 @@ package com.artillexstudios.axminions.listeners
import com.artillexstudios.axapi.utils.StringUtils import com.artillexstudios.axapi.utils.StringUtils
import com.artillexstudios.axminions.AxMinionsPlugin import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.AxMinionsAPI import com.artillexstudios.axminions.api.AxMinionsAPI
import com.artillexstudios.axminions.api.config.Config
import com.artillexstudios.axminions.api.config.Messages import com.artillexstudios.axminions.api.config.Messages
import com.artillexstudios.axminions.api.minions.Direction import com.artillexstudios.axminions.api.minions.Direction
import com.artillexstudios.axminions.api.minions.Minion import com.artillexstudios.axminions.api.minions.Minion
@ -10,7 +11,8 @@ import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import com.artillexstudios.axminions.api.utils.CoolDown import com.artillexstudios.axminions.api.utils.CoolDown
import com.artillexstudios.axminions.api.utils.Keys import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor import com.artillexstudios.axminions.api.utils.fastFor
import java.util.UUID import net.md_5.bungee.api.ChatMessageType
import net.md_5.bungee.api.chat.TextComponent
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -22,7 +24,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
class MinionInventoryListener : Listener { class MinionInventoryListener : Listener {
private val coolDown = CoolDown<UUID>() private val coolDown = CoolDown<Player>()
@EventHandler @EventHandler
fun onInventoryDragEvent(event: InventoryDragEvent) { fun onInventoryDragEvent(event: InventoryDragEvent) {
@ -38,11 +40,11 @@ class MinionInventoryListener : Listener {
event.isCancelled = true event.isCancelled = true
val player = event.whoClicked as Player val player = event.whoClicked as Player
if (coolDown.contains(player.uniqueId)) { if (coolDown.contains(player)) {
return return
} }
coolDown.add(player.uniqueId, 250) coolDown.add(player, 250)
val allowedTools = arrayListOf<Material>() val allowedTools = arrayListOf<Material>()
minion.getType().getConfig().getStringList("tool.material").fastFor { minion.getType().getConfig().getStringList("tool.material").fastFor {
@ -116,7 +118,7 @@ class MinionInventoryListener : Listener {
} }
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.LINK_START())) player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.LINK_START()))
LinkingListener.linking[player.uniqueId] = minion LinkingListener.linking[player] = minion
player.closeInventory() player.closeInventory()
} }
@ -129,13 +131,13 @@ class MinionInventoryListener : Listener {
} }
if (minion.getActionAmount() < actions) { if (minion.getActionAmount() < actions) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.UPGRADE_FAIL())) sendFail(player)
return return
} }
AxMinionsPlugin.integrations.getEconomyIntegration()?.let { AxMinionsPlugin.integrations.getEconomyIntegration()?.let {
if (it.getBalance(player) < money) { if (it.getBalance(player) < money) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.UPGRADE_FAIL())) sendFail(player)
return return
} }
@ -154,8 +156,8 @@ class MinionInventoryListener : Listener {
if (minion.getType() == MinionTypes.getMinionTypes()["seller"]) { if (minion.getType() == MinionTypes.getMinionTypes()["seller"]) {
AxMinionsPlugin.integrations.getEconomyIntegration()?.let { AxMinionsPlugin.integrations.getEconomyIntegration()?.let {
minion.getOwner()?.let { minion.getOwner()?.let { player ->
player -> it.giveBalance(player, stored) it.giveBalance(player, stored)
minion.setStorage(0.0) minion.setStorage(0.0)
} }
} }
@ -175,4 +177,30 @@ class MinionInventoryListener : Listener {
holder.removeOpenInventory(event.inventory) holder.removeOpenInventory(event.inventory)
} }
private fun sendFail(player: Player) {
when (Config.UPGRADE_FAIL()) {
"title" -> {
player.closeInventory()
player.sendTitle(StringUtils.formatToString(Messages.UPGRADE_FAIL()), "", 10, 70, 20)
}
"subtitle" -> {
player.closeInventory()
player.sendTitle("", StringUtils.formatToString(Messages.UPGRADE_FAIL()), 10, 70, 20)
}
"actionbar" -> {
player.closeInventory()
player.spigot().sendMessage(
ChatMessageType.ACTION_BAR,
*TextComponent.fromLegacyText(StringUtils.formatToString(Messages.UPGRADE_FAIL()))
)
}
else -> {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.UPGRADE_FAIL()))
}
}
}
} }

View File

@ -51,6 +51,7 @@ class MinionPlaceListener : Listener {
val maxMinions = AxMinionsAPI.INSTANCE.getMinionLimit(event.player) val maxMinions = AxMinionsAPI.INSTANCE.getMinionLimit(event.player)
val chunk = location.chunk
AxMinionsPlugin.dataQueue.submit { AxMinionsPlugin.dataQueue.submit {
val placed = AxMinionsPlugin.dataHandler.getMinionAmount(event.player.uniqueId) val placed = AxMinionsPlugin.dataHandler.getMinionAmount(event.player.uniqueId)
@ -85,7 +86,8 @@ class MinionPlaceListener : Listener {
locationId, locationId,
0 0
) )
minion.setTicking(true) Minions.addTicking(chunk)
Scheduler.get().run { task -> Scheduler.get().run { task ->
meta = item.itemMeta!! meta = item.itemMeta!!
meta.persistentDataContainer.remove(Keys.PLACED) meta.persistentDataContainer.remove(Keys.PLACED)
@ -97,7 +99,7 @@ class MinionPlaceListener : Listener {
event.player.sendMessage( event.player.sendMessage(
"Placed minion $minion. Ticking? ${minion.isTicking()} Is chunk ticking? ${ "Placed minion $minion. Ticking? ${minion.isTicking()} Is chunk ticking? ${
Minions.isTicking( Minions.isTicking(
location.chunk chunk
) )
}" }"
) )

View File

@ -1,6 +1,5 @@
package com.artillexstudios.axminions.listeners package com.artillexstudios.axminions.listeners
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
@ -10,8 +9,6 @@ class WorldListener : Listener {
@EventHandler @EventHandler
fun onWorldLoadEvent(event: WorldLoadEvent) { fun onWorldLoadEvent(event: WorldLoadEvent) {
AxMinionsPlugin.dataQueue.submit { MinionTypes.loadForWorld(event.world)
MinionTypes.loadForWorld(event.world)
}
} }
} }

View File

@ -67,6 +67,7 @@ class Minion(
private val extraData = hashMapOf<String, String>() private val extraData = hashMapOf<String, String>()
private var linkedInventory: Inventory? = null private var linkedInventory: Inventory? = null
internal val openInventories = mutableListOf<Inventory>() internal val openInventories = mutableListOf<Inventory>()
@Volatile
private var ticking = false private var ticking = false
private var debugHologram: Hologram? = null private var debugHologram: Hologram? = null
private var broken = false private var broken = false
@ -74,12 +75,6 @@ class Minion(
init { init {
spawn() spawn()
Minions.load(this) Minions.load(this)
if (linkedChest != null) {
Scheduler.get().runAt(linkedChest) {
linkedInventory = (linkedChest?.block?.state as? Container)?.inventory
}
}
} }
override fun getType(): MinionType { override fun getType(): MinionType {
@ -149,7 +144,7 @@ class Minion(
} }
private fun breakMinion(event: PacketEntityInteractEvent) { private fun breakMinion(event: PacketEntityInteractEvent) {
LinkingListener.linking.remove(event.player.uniqueId) LinkingListener.linking.remove(event.player)
remove() remove()
setTicking(false) setTicking(false)
openInventories.fastFor { it.viewers.fastFor { viewer -> viewer.closeInventory() } } openInventories.fastFor { it.viewers.fastFor { viewer -> viewer.closeInventory() } }
@ -307,7 +302,7 @@ class Minion(
} }
override fun openInventory(player: Player) { override fun openInventory(player: Player) {
LinkingListener.linking.remove(player.uniqueId) LinkingListener.linking.remove(player)
val inventory = Bukkit.createInventory( val inventory = Bukkit.createInventory(
this, this,
Config.GUI_SIZE(), Config.GUI_SIZE(),
@ -565,10 +560,14 @@ class Minion(
override fun setTicking(ticking: Boolean) { override fun setTicking(ticking: Boolean) {
this.ticking = ticking this.ticking = ticking
if (ticking && linkedChest != null) { if (linkedChest == null) return
if (ticking) {
Scheduler.get().runAt(linkedChest) { Scheduler.get().runAt(linkedChest) {
linkedInventory = (linkedChest?.block?.state as? Container)?.inventory linkedInventory = (linkedChest?.block?.state as? Container)?.inventory
} }
} else {
linkedInventory = null
} }
} }

View File

@ -9,6 +9,7 @@ object MinionTicker {
private inline fun tickAll() { private inline fun tickAll() {
Minions.get { minions -> Minions.get { minions ->
minions.fastFor { pos -> minions.fastFor { pos ->
if (!pos.ticking) return@fastFor
pos.minions.fastFor { pos.minions.fastFor {
it.tick() it.tick()
} }

View File

@ -37,7 +37,7 @@ object Minions {
lock.read { lock.read {
minions.forEach { minions.forEach {
if (world.uid == it.worldUUID && it.x == chunkX && it.z == chunkZ) { if (world.uid == it.worldUUID && it.x == chunkX && it.z == chunkZ) {
return true return it.ticking
} }
} }
} }
@ -63,8 +63,8 @@ object Minions {
} }
fun load(minion: Minion) { fun load(minion: Minion) {
val chunkX = round(minion.getLocation().x) shr 4 val chunkX = (Math.round(minion.getLocation().x) shr 4).toInt()
val chunkZ = round(minion.getLocation().z) shr 4 val chunkZ = (Math.round(minion.getLocation().z) shr 4).toInt()
val world = minion.getLocation().world ?: return val world = minion.getLocation().world ?: return
lock.write { lock.write {
@ -79,7 +79,7 @@ object Minions {
} }
if (pos === null) { if (pos === null) {
pos = ChunkPos(world, chunkX, chunkZ) pos = ChunkPos(world, chunkX, chunkZ, false)
minions.add(pos!!) minions.add(pos!!)
} }
@ -89,8 +89,8 @@ object Minions {
} }
fun remove(minion: Minion) { fun remove(minion: Minion) {
val chunkX = round(minion.getLocation().x) shr 4 val chunkX = (Math.round(minion.getLocation().x) shr 4).toInt()
val chunkZ = round(minion.getLocation().z) shr 4 val chunkZ = (Math.round(minion.getLocation().z) shr 4).toInt()
val world = minion.getLocation().world ?: return val world = minion.getLocation().world ?: return
lock.write { lock.write {
@ -124,8 +124,4 @@ object Minions {
minions(this.minions) minions(this.minions)
} }
} }
private infix fun round(double: Double): Int {
return (double + 0.5).toInt()
}
} }

View File

@ -52,9 +52,9 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
Warnings.remove(minion, Warnings.NO_TOOL) Warnings.remove(minion, Warnings.NO_TOOL)
var size = 0 var size = 0
val drops = arrayListOf<ItemStack>()
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location -> LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val block = location.block val block = location.block
val drops = arrayListOf<ItemStack>()
when (block.type) { when (block.type) {
Material.CACTUS, Material.SUGAR_CANE, Material.BAMBOO -> { Material.CACTUS, Material.SUGAR_CANE, Material.BAMBOO -> {
@ -95,10 +95,10 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
else -> return@fastFor else -> return@fastFor
} }
minion.addToContainerOrDrop(drops)
minion.damageTool(size)
minion.setActions(minion.getActionAmount() + size)
} }
minion.addToContainerOrDrop(drops)
minion.damageTool(size)
minion.setActions(minion.getActionAmount() + size)
} }
} }

View File

@ -39,6 +39,11 @@ use-durability: true
# If the minion should pull new tools from the chest it's connected to # If the minion should pull new tools from the chest it's connected to
pull-tools-from-chest: false pull-tools-from-chest: false
# What type of message we should send when the upgrade fails
# due to insufficient funds
# Possible options: chat, title, subtitle, actionbar
upgrade-fail: "chat"
database: database:
# Can be H2 (SqLite support is planned) # Can be H2 (SqLite support is planned)
type: "H2" type: "H2"
@ -90,4 +95,4 @@ gui:
debug: false debug: false
# Do not change! # Do not change!
config-version: 2 config-version: 3