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 java.io.File
import java.io.InputStream
import java.util.Locale
class Config(file: File, stream: InputStream) {
companion object {
@ -42,6 +43,8 @@ class Config(file: File, stream: InputStream) {
@JvmStatic
fun PULL_FROM_CHEST() = AxMinionsAPI.INSTANCE.getConfig().get("pull-tools-from-chest", false)
@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)
@JvmStatic
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.minions.Minion
import com.artillexstudios.axminions.api.utils.Keys
import com.artillexstudios.axminions.api.utils.fastFor
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import java.io.File
@ -33,12 +32,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
return true
}
fun isTicking(minion: Minion): Boolean {
return minion.isTicking()
}
fun tick(minion: Minion) {
if (!minion.isTicking()) return
if (!shouldRun(minion)) return
minion.resetAnimation()

View File

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

View File

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

View File

@ -123,7 +123,7 @@ allprojects {
compileOnly 'com.intellectualsites.plotsquared:plotsquared-core: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("com.artillexstudios.axapi:axapi:1.4.22")
implementation("com.artillexstudios.axapi:axapi:1.4.23")
implementation("net.byteflux:libby-bukkit:1.3.0")
implementation("com.zaxxer:HikariCP:5.1.0")
implementation("org.bstats:bstats-bukkit:3.0.2")

View File

@ -82,7 +82,7 @@ class AxMinionsCommand {
val total = minions.size
minions.fastFor {
if (it.getType().isTicking(it)) {
if (it.isTicking()) {
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.Messages
import com.artillexstudios.axminions.api.minions.Minion
import java.util.UUID
import java.util.WeakHashMap
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEvent
class LinkingListener : Listener {
companion object {
val linking = hashMapOf<UUID, Minion>()
val linking = WeakHashMap<Player, Minion>()
private val CONTAINERS = listOf(Material.BARREL, Material.CHEST, Material.TRAPPED_CHEST)
}
@EventHandler
fun onPlayerInteractEvent(event: PlayerInteractEvent) {
if (event.player.uniqueId !in linking) return
if (event.clickedBlock == null) return
if (event.player !in linking) return
if (event.clickedBlock!!.type !in CONTAINERS) 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
if (minion.getLocation()
.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.axminions.AxMinionsPlugin
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.minions.Direction
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.Keys
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.entity.Player
import org.bukkit.event.EventHandler
@ -22,7 +24,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
class MinionInventoryListener : Listener {
private val coolDown = CoolDown<UUID>()
private val coolDown = CoolDown<Player>()
@EventHandler
fun onInventoryDragEvent(event: InventoryDragEvent) {
@ -38,11 +40,11 @@ class MinionInventoryListener : Listener {
event.isCancelled = true
val player = event.whoClicked as Player
if (coolDown.contains(player.uniqueId)) {
if (coolDown.contains(player)) {
return
}
coolDown.add(player.uniqueId, 250)
coolDown.add(player, 250)
val allowedTools = arrayListOf<Material>()
minion.getType().getConfig().getStringList("tool.material").fastFor {
@ -116,7 +118,7 @@ class MinionInventoryListener : Listener {
}
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.LINK_START()))
LinkingListener.linking[player.uniqueId] = minion
LinkingListener.linking[player] = minion
player.closeInventory()
}
@ -129,13 +131,13 @@ class MinionInventoryListener : Listener {
}
if (minion.getActionAmount() < actions) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.UPGRADE_FAIL()))
sendFail(player)
return
}
AxMinionsPlugin.integrations.getEconomyIntegration()?.let {
if (it.getBalance(player) < money) {
player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.UPGRADE_FAIL()))
sendFail(player)
return
}
@ -154,8 +156,8 @@ class MinionInventoryListener : Listener {
if (minion.getType() == MinionTypes.getMinionTypes()["seller"]) {
AxMinionsPlugin.integrations.getEconomyIntegration()?.let {
minion.getOwner()?.let {
player -> it.giveBalance(player, stored)
minion.getOwner()?.let { player ->
it.giveBalance(player, stored)
minion.setStorage(0.0)
}
}
@ -175,4 +177,30 @@ class MinionInventoryListener : Listener {
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 chunk = location.chunk
AxMinionsPlugin.dataQueue.submit {
val placed = AxMinionsPlugin.dataHandler.getMinionAmount(event.player.uniqueId)
@ -85,7 +86,8 @@ class MinionPlaceListener : Listener {
locationId,
0
)
minion.setTicking(true)
Minions.addTicking(chunk)
Scheduler.get().run { task ->
meta = item.itemMeta!!
meta.persistentDataContainer.remove(Keys.PLACED)
@ -97,7 +99,7 @@ class MinionPlaceListener : Listener {
event.player.sendMessage(
"Placed minion $minion. Ticking? ${minion.isTicking()} Is chunk ticking? ${
Minions.isTicking(
location.chunk
chunk
)
}"
)

View File

@ -1,6 +1,5 @@
package com.artillexstudios.axminions.listeners
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.miniontype.MinionTypes
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
@ -10,8 +9,6 @@ class WorldListener : Listener {
@EventHandler
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 var linkedInventory: Inventory? = null
internal val openInventories = mutableListOf<Inventory>()
@Volatile
private var ticking = false
private var debugHologram: Hologram? = null
private var broken = false
@ -74,12 +75,6 @@ class Minion(
init {
spawn()
Minions.load(this)
if (linkedChest != null) {
Scheduler.get().runAt(linkedChest) {
linkedInventory = (linkedChest?.block?.state as? Container)?.inventory
}
}
}
override fun getType(): MinionType {
@ -149,7 +144,7 @@ class Minion(
}
private fun breakMinion(event: PacketEntityInteractEvent) {
LinkingListener.linking.remove(event.player.uniqueId)
LinkingListener.linking.remove(event.player)
remove()
setTicking(false)
openInventories.fastFor { it.viewers.fastFor { viewer -> viewer.closeInventory() } }
@ -307,7 +302,7 @@ class Minion(
}
override fun openInventory(player: Player) {
LinkingListener.linking.remove(player.uniqueId)
LinkingListener.linking.remove(player)
val inventory = Bukkit.createInventory(
this,
Config.GUI_SIZE(),
@ -565,10 +560,14 @@ class Minion(
override fun setTicking(ticking: Boolean) {
this.ticking = ticking
if (ticking && linkedChest != null) {
if (linkedChest == null) return
if (ticking) {
Scheduler.get().runAt(linkedChest) {
linkedInventory = (linkedChest?.block?.state as? Container)?.inventory
}
} else {
linkedInventory = null
}
}

View File

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

View File

@ -37,7 +37,7 @@ object Minions {
lock.read {
minions.forEach {
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) {
val chunkX = round(minion.getLocation().x) shr 4
val chunkZ = round(minion.getLocation().z) shr 4
val chunkX = (Math.round(minion.getLocation().x) shr 4).toInt()
val chunkZ = (Math.round(minion.getLocation().z) shr 4).toInt()
val world = minion.getLocation().world ?: return
lock.write {
@ -79,7 +79,7 @@ object Minions {
}
if (pos === null) {
pos = ChunkPos(world, chunkX, chunkZ)
pos = ChunkPos(world, chunkX, chunkZ, false)
minions.add(pos!!)
}
@ -89,8 +89,8 @@ object Minions {
}
fun remove(minion: Minion) {
val chunkX = round(minion.getLocation().x) shr 4
val chunkZ = round(minion.getLocation().z) shr 4
val chunkX = (Math.round(minion.getLocation().x) shr 4).toInt()
val chunkZ = (Math.round(minion.getLocation().z) shr 4).toInt()
val world = minion.getLocation().world ?: return
lock.write {
@ -124,8 +124,4 @@ object 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)
var size = 0
val drops = arrayListOf<ItemStack>()
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val block = location.block
val drops = arrayListOf<ItemStack>()
when (block.type) {
Material.CACTUS, Material.SUGAR_CANE, Material.BAMBOO -> {
@ -95,10 +95,10 @@ class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResour
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
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:
# Can be H2 (SqLite support is planned)
type: "H2"
@ -90,4 +95,4 @@ gui:
debug: false
# Do not change!
config-version: 2
config-version: 3