Fixes, rewrite data handling

This commit is contained in:
TomTom 2023-10-07 19:34:34 +02:00
parent 343e0cb172
commit d2b3defbd6
11 changed files with 329 additions and 82 deletions

View File

@ -3,6 +3,7 @@ package com.artillexstudios.axminions.api.data
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import org.bukkit.Location
import org.bukkit.World
import java.util.UUID
interface DataHandler {
@ -11,8 +12,16 @@ interface DataHandler {
fun setup()
fun insertType(minionType: MinionType)
fun loadMinionsOfType(minionType: MinionType)
fun getLocationID(location: Location): Int
fun getLocation(locationId: Int): Location?
fun getWorld(worldId: Int): World?
fun saveMinion(minion: Minion)
fun deleteMinion(minion: Minion)

View File

@ -29,7 +29,9 @@ interface Minion {
fun getLevel(): Int
fun storeData(key: String, value: String?)
fun setActions(actions: Long)
fun setStorage(storage: Double)
fun setWarning(warning: Warning?)
@ -57,6 +59,10 @@ interface Minion {
fun getNextAction(): Int
fun getActionAmount(): Long
fun getStorage(): Double
fun getRange(): Double
fun resetAnimation()
@ -80,4 +86,10 @@ interface Minion {
fun addToContainerOrDrop(itemStack: ItemStack)
fun addToContainerOrDrop(itemStack: Iterable<ItemStack>)
fun updateArmour()
fun getLocationId(): Int
fun getChestLocationId(): Int
}

View File

@ -16,6 +16,7 @@ abstract class MinionType(private val name: String, private val defaults: InputS
fun load() {
config = Config(File(AxMinionsAPI.INSTANCE.getAxMinionsDataFolder(), "/minions/$name.yml"), defaults)
AxMinionsAPI.INSTANCE.getDataHandler().insertType(this)
AxMinionsAPI.INSTANCE.getDataHandler().loadMinionsOfType(this)
}
@ -38,10 +39,6 @@ abstract class MinionType(private val name: String, private val defaults: InputS
run(minion)
}
fun updateArmor(minion: Minion) {
}
fun getItem(level: Int = 1): ItemStack {
val builder = ItemBuilder(config.getSection("item"))
builder.storePersistentData(MinionTypes.getMinionKey(), PersistentDataType.STRING, name)

View File

@ -36,4 +36,11 @@ class AxMinionsCommand {
sender.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.RELOAD_SUCCESS(), Placeholder.unparsed("time", (System.currentTimeMillis() - start).toString())))
}
@Subcommand("convert")
@CommandPermission("axminions.command.convert")
@Description("Convert from a different plugin")
fun convert(sender: CommandSender, ) {
}
}

View File

@ -2,15 +2,18 @@ package com.artillexstudios.axminions.data
import com.artillexstudios.axapi.serializers.Serializers
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.data.DataHandler
import com.artillexstudios.axminions.api.minions.Direction
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.data.DataHandler
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.World
import org.bukkit.inventory.ItemStack
import org.h2.jdbc.JdbcConnection
import java.sql.Connection
import java.sql.Statement
import java.sql.Types
import java.util.Properties
import java.util.UUID
@ -25,30 +28,67 @@ class H2DataHandler : DataHandler {
connection =
JdbcConnection("jdbc:h2:./${AxMinionsPlugin.INSTANCE.dataFolder}/data", Properties(), null, null, false)
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_data`(`location` VARCHAR(256) NOT NULL, `owner` VARCHAR(36) NOT NULL, `linked-chest-location` VARCHAR(256) DEFAULT NULL, `extra_data` CLOB DEFAULT NULL, `direction` TINYINT NOT NULL DEFAULT '0', `type` VARCHAR(64) NOT NULL, `level` SMALLINT NOT NULL DEFAULT '1', `tool` CLOB DEFAULT NULL, PRIMARY KEY(`location`));")
.use { preparedStatement ->
preparedStatement.executeUpdate()
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_types`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_users`(`uuid` UUID PRIMARY KEY, `name` VARCHAR(16));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_worlds`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(64));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_locations`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `x` INT, `y` INT, `z` INT, `world_id` INT, FOREIGN KEY(world_id) REFERENCES `axminions_worlds`(`id`));")
.use {
it.executeUpdate()
}
connection.prepareStatement("CREATE TABLE IF NOT EXISTS `axminions_minions`(`id` BIGINT AUTO_INCREMENT PRIMARY KEY, `location_id` INT, `chest_location_id` INT, `owner_id` UUID, `type_id` INT, `direction` TINYINT, `level` SMALLINT, `storage` DOUBLE, `actions` BIGINT, `tool` CLOB, FOREIGN KEY(`location_id`) REFERENCES `axminions_locations`(id), FOREIGN KEY(`chest_location_id`) REFERENCES `axminions_locations`(`id`), FOREIGN KEY(`owner_id`) REFERENCES `axminions_users`(`uuid`), FOREIGN KEY(`type_id`) REFERENCES `axminions_types`(`id`));")
.use {
it.executeUpdate()
}
}
override fun loadMinionsOfType(minionType: MinionType) {
connection.prepareStatement("SELECT * FROM `axminions_data` WHERE `type` = ?;").use { preparedStatement ->
preparedStatement.setString(1, minionType.getName())
preparedStatement.executeQuery().use { resultSet ->
while (resultSet.next()) {
val location = resultSet.getString("location")
val owner = resultSet.getString("owner")
val direction = Direction.entries[resultSet.getInt("direction")]
val level = resultSet.getInt("level")
val tool = resultSet.getString("tool")
val linkedChest = resultSet.getString("linked-chest-location")
val extraData = resultSet.getString("extra_data")
val uuid = UUID.fromString(owner)
val ownerPlayer = Bukkit.getOfflinePlayer(uuid)
override fun insertType(minionType: MinionType) {
connection.prepareStatement("MERGE INTO `axminions_types`(`name`) KEY(`name`) VALUES(?);").use {
it.setString(1, minionType.getName())
it.executeUpdate()
}
}
var linkedChestLocation: Location? = null
if (linkedChest != null) {
linkedChestLocation = Serializers.LOCATION.deserialize(linkedChest)
override fun loadMinionsOfType(minionType: MinionType) {
var typeId = 0
connection.prepareStatement("SELECT `id` FROM `axminions_types` WHERE `name` = ?;").use { statement ->
statement.setString(1, minionType.getName())
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
typeId = resultSet.getInt("id")
}
}
}
connection.prepareStatement("SELECT * FROM `axminions_minions` WHERE `type_id` = ?;").use { statement ->
statement.setInt(1, typeId)
statement.executeQuery().use { resultSet ->
while (resultSet.next()) {
val locationId = resultSet.getInt("location_id")
val chestLocationId = resultSet.getInt("chest_location_id")
val ownerId = resultSet.getObject("owner_id") as UUID
val direction = Direction.entries[resultSet.getByte("direction").toInt()]
val level = resultSet.getShort("level")
val storage = resultSet.getDouble("storage")
val actions = resultSet.getLong("actions")
val tool = resultSet.getString("tool")
val location = getLocation(locationId)
var chestLocation: Location? = null
if (chestLocationId != 0) {
chestLocation = getLocation(chestLocationId)
}
var itemStack = ItemStack(Material.AIR)
@ -57,55 +97,190 @@ class H2DataHandler : DataHandler {
}
com.artillexstudios.axminions.minions.Minion(
Serializers.LOCATION.deserialize(location),
uuid,
ownerPlayer,
location!!,
ownerId,
Bukkit.getOfflinePlayer(ownerId),
minionType,
level,
level.toInt(),
itemStack,
linkedChestLocation,
chestLocation,
direction,
extraData
actions,
storage,
locationId,
chestLocationId
)
}
}
}
}
override fun saveMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
connection.prepareStatement("MERGE INTO `axminions_data`(`location`, `owner`, `linked-chest-location`, `extra_data`, `direction`, `type`, `level`, `tool`) KEY(`location`) VALUES(?,?,?,?,?,?,?,?);")
.use { preparedStatement ->
preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation()))
preparedStatement.setString(2, minion.getOwnerUUID().toString())
preparedStatement.setString(
3,
when (minion.getLinkedChest()) {
null -> null
else -> Serializers.LOCATION.serialize(minion.getLinkedChest())
}
)
preparedStatement.setString(4, minion.serializeExtraData())
preparedStatement.setInt(5, minion.getDirection().ordinal)
preparedStatement.setString(6, minion.getType().getName())
preparedStatement.setInt(7, minion.getLevel())
preparedStatement.setString(8, Serializers.ITEM_STACK.serialize(minion.getTool()))
override fun getLocationID(location: Location): Int {
var worldId = 0
connection.prepareStatement(
"MERGE INTO `axminions_worlds`(`name`) KEY(`name`) VALUES(?);",
Statement.RETURN_GENERATED_KEYS
).use { statement ->
statement.setString(1, location.world?.name)
statement.executeUpdate()
preparedStatement.executeUpdate()
statement.generatedKeys.use { resultSet ->
if (resultSet.next()) {
worldId = resultSet.getInt(1)
}
}
}
connection.prepareStatement(
"MERGE INTO `axminions_locations`(`x`, `y`, `z`, `world_id`) KEY(`x`, `y`, `z`, `world_id`) VALUES (?, ?, ?, ?);",
Statement.RETURN_GENERATED_KEYS
).use { statement ->
statement.setInt(1, location.blockX)
statement.setInt(2, location.blockY)
statement.setInt(3, location.blockZ)
statement.setInt(4, worldId)
statement.executeUpdate()
statement.generatedKeys.use { resultSet ->
if (resultSet.next()) {
return resultSet.getInt(1)
}
}
}
return 0
}
override fun getLocation(locationId: Int): Location? {
connection.prepareStatement("SELECT `name` FROM `axminions_locations` WHERE `id` = ?").use { statement ->
statement.setInt(1, locationId)
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
val worldId = resultSet.getInt("world_id")
val x = resultSet.getInt("x")
val y = resultSet.getInt("y")
val z = resultSet.getInt("z")
return Location(getWorld(worldId), x.toDouble(), y.toDouble(), z.toDouble())
}
return null
}
}
}
override fun getWorld(worldId: Int): World? {
connection.prepareStatement("SELECT `name` FROM `axminions_worlds` WHERE `id` = ?").use { statement ->
statement.setInt(1, worldId)
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
return Bukkit.getWorld(resultSet.getString("name"))!!
}
return null
}
}
}
override fun saveMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
val locationId = getLocationID(minion.getLocation())
var linkedChestId: Int? = null
var userId: Int? = null
var minionTypeId = 0
connection.prepareStatement("SELECT * FROM `axminions_types` WHERE `name` = ?;").use {
it.setString(1, minion.getType().getName())
it.executeQuery().use { resultSet ->
if (resultSet.next()) {
minionTypeId = resultSet.getInt("id")
}
}
}
if (minion.getLinkedChest() != null) {
linkedChestId = getLocationID(minion.getLinkedChest()!!)
}
connection.prepareStatement(
"MERGE INTO `axminions_users`(`uuid`, `name`) KEY(`uuid`) VALUES (?,?);",
Statement.RETURN_GENERATED_KEYS
).use { statement ->
statement.setObject(1, minion.getOwnerUUID())
statement.setString(2, minion.getOwner()?.name ?: "---")
statement.executeUpdate()
statement.generatedKeys.use { resultSet ->
if (resultSet.next()) {
if (resultSet.next()) {
userId = resultSet.getInt(1)
}
}
}
}
if (userId == null) {
return
}
connection.prepareStatement("MERGE INTO `axminions_minions`(`location_id`, `chest_location_id`, `owner_id`, `type_id`, `direction`, `level`, `storage`, `actions`, `tool`) KEY(`location_id`) VALUES(?,?,?,?,?,?,?,?,?)")
.use { statement ->
statement.setInt(1, locationId)
if (linkedChestId == null) {
statement.setNull(2, Types.INTEGER)
} else {
statement.setInt(2, linkedChestId)
}
statement.setInt(3, userId!!)
statement.setInt(4, minionTypeId)
statement.setByte(5, minion.getDirection().ordinal.toByte())
statement.setInt(6, minion.getLevel())
statement.setDouble(7, minion.getStorage())
statement.setLong(8, minion.getActionAmount())
statement.setString(9, Serializers.ITEM_STACK.serialize(minion.getTool()))
statement.executeUpdate()
}
}
override fun deleteMinion(minion: com.artillexstudios.axminions.api.minions.Minion) {
connection.prepareStatement("DELETE FROM `axminions_data` WHERE `location` = ?;").use { preparedStatement ->
preparedStatement.setString(1, Serializers.LOCATION.serialize(minion.getLocation()))
preparedStatement.executeUpdate()
connection.prepareStatement("DELETE FROM `axminions_minions` WHERE `location_id` = ?;")
.use { preparedStatement ->
preparedStatement.setInt(1, minion.getLocationId())
preparedStatement.executeUpdate()
}
connection.prepareStatement("SELECT * FROM `axminions_minions` WHERE `location_id` = ?;").use { statement ->
statement.setInt(1, minion.getLocationId())
statement.executeQuery().use { resultSet ->
if (!resultSet.next()) {
connection.prepareStatement("DELETE FROM `axminions_locations` WHERE `id` = ?").use {
it.setInt(1, minion.getLocationId())
it.executeUpdate()
}
}
}
}
if (minion.getChestLocationId() != 0) {
connection.prepareStatement("SELECT * FROM `axminions_minions` WHERE `chest_location_id` = ?;")
.use { statement ->
statement.setInt(1, minion.getChestLocationId())
statement.executeQuery().use { resultSet ->
if (!resultSet.next()) {
connection.prepareStatement("DELETE FROM `axminions_locations` WHERE `id` = ?").use {
it.setInt(1, minion.getLocationId())
it.executeUpdate()
}
}
}
}
}
}
override fun getMinionAmount(uuid: UUID): Int {
connection.prepareStatement("SELECT COUNT(`owner`) FROM `axminions_data` WHERE `owner` = ?;")
.use { preparedStatement ->
preparedStatement.setString(1, uuid.toString())
preparedStatement.executeQuery().use { resultSet ->
connection.prepareStatement("SELECT COUNT(`owner_id`) FROM `axminions_minions` WHERE `owner_id` = (SELECT `owner_id` FROM `axminions_users` WHERE `uuid` = ?);")
.use { statement ->
statement.setObject(1, uuid)
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
return resultSet.getInt(1)
}
@ -116,14 +291,18 @@ class H2DataHandler : DataHandler {
}
override fun isMinion(location: Location): Boolean {
connection.prepareStatement("SELECT * FROM `axminions_data` WHERE `location` = ?;").use { preparedStatement ->
preparedStatement.setString(1, Serializers.LOCATION.serialize(location))
preparedStatement.executeQuery().use { resultSet ->
if (resultSet.next()) {
return true
connection.prepareStatement("SELECT * FROM `axminions_minions` WHERE `location_id` = (SELECT `id` FROM `axminions_locations` WHERE x = ? AND y = ? AND z = ? AND `world_id` = (SELECT `id` FROM `axminions_worlds` WHERE `name` = ?));")
.use { statement ->
statement.setInt(1, location.blockX)
statement.setInt(2, location.blockY)
statement.setInt(3, location.blockZ)
statement.setString(4, location.world?.name ?: "---")
statement.executeQuery().use { resultSet ->
if (resultSet.next()) {
return true
}
}
}
}
return false
}

View File

@ -47,7 +47,7 @@ class MinionPlaceListener : Listener {
return@submit
}
val minion = Minion(location, event.player.uniqueId, event.player, minionType, 1, ItemStack(Material.AIR), null, Direction.NORTH, "")
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)
AxMinionsPlugin.dataHandler.saveMinion(minion)
event.player.sendMessage(StringUtils.formatToString(Messages.PREFIX() + Messages.PLACE_SUCCESS()))

View File

@ -4,13 +4,14 @@ import com.artillexstudios.axapi.entity.PacketEntityFactory
import com.artillexstudios.axapi.entity.impl.PacketArmorStand
import com.artillexstudios.axapi.entity.impl.PacketEntity
import com.artillexstudios.axapi.hologram.Hologram
import com.artillexstudios.axapi.utils.EquipmentSlot
import com.artillexstudios.axapi.utils.ItemBuilder
import com.artillexstudios.axapi.utils.RotationType
import com.artillexstudios.axminions.api.minions.Direction
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.api.warnings.Warning
import com.artillexstudios.axminions.api.warnings.Warnings
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.OfflinePlayer
@ -33,7 +34,10 @@ class Minion(
private var tool: ItemStack?,
private var linkedChest: Location?,
private var direction: Direction,
savedExtraData: String
private var actions: Long,
private var storage: Double,
private val locationID: Int,
private var chestLocationId: Int
) : Minion {
private lateinit var entity: PacketArmorStand
private var nextAction = 0
@ -47,7 +51,6 @@ class Minion(
init {
spawn()
loadExtraData(savedExtraData)
Minions.load(this)
linkedInventory = (linkedChest?.block?.blockData as? Container)?.inventory
}
@ -60,6 +63,7 @@ class Minion(
entity = PacketEntityFactory.get().spawnEntity(location, EntityType.ARMOR_STAND) as PacketArmorStand
entity.setHasBasePlate(false)
entity.setSmall(true)
updateArmour()
entity.onClick { event ->
if (event.isAttack) {
println("LEFT CLICKED!")
@ -94,20 +98,19 @@ class Minion(
}
override fun getAsItem(): ItemStack {
return ItemStack(Material.STONE)
return type.getItem(level)
}
override fun getLevel(): Int {
return this.level
}
override fun storeData(key: String, value: String?) {
if (value == null) {
extraData.remove(key)
return
}
override fun setActions(actions: Long) {
this.actions = actions
}
extraData[key] = value
override fun setStorage(storage: Double) {
this.storage = storage
}
override fun setWarning(warning: Warning?) {
@ -162,6 +165,14 @@ class Minion(
return this.nextAction
}
override fun getActionAmount(): Long {
return this.actions
}
override fun getStorage(): Double {
return this.storage
}
override fun getRange(): Double {
return this.range
}
@ -232,12 +243,37 @@ class Minion(
}
}
private fun loadExtraData(data: String) {
data.split("|").forEach { split ->
val secondSplit = split.split("=")
if (secondSplit.isNotEmpty()) {
extraData[secondSplit[0]] = secondSplit[1]
}
override fun updateArmour() {
for (entry in EquipmentSlot.entries) {
entity.setItem(entry, ItemStack(Material.AIR))
}
type.getSection("items.helmet", level)?.let {
println("helmet!")
entity.setItem(EquipmentSlot.HELMET, ItemBuilder(it).get())
}
type.getSection("items.chestplate", level)?.let {
println("CP!")
entity.setItem(EquipmentSlot.CHEST_PLATE, ItemBuilder(it).get())
}
type.getSection("items.leggings", level)?.let {
println("legs!")
entity.setItem(EquipmentSlot.LEGGINGS, ItemBuilder(it).get())
}
type.getSection("items.boots", level)?.let {
println("boots!")
entity.setItem(EquipmentSlot.BOOTS, ItemBuilder(it).get())
}
}
override fun getLocationId(): Int {
return this.locationID
}
override fun getChestLocationId(): Int {
return this.chestLocationId
}
}

View File

@ -3,6 +3,7 @@ package com.artillexstudios.axminions.minions.miniontype
import com.artillexstudios.axminions.AxMinionsPlugin
import com.artillexstudios.axminions.api.minions.Minion
import com.artillexstudios.axminions.api.minions.miniontype.MinionType
import com.artillexstudios.axminions.minions.MinionTicker
import com.artillexstudios.axminions.utils.LocationUtils
import com.artillexstudios.axminions.utils.fastFor
import org.bukkit.Material
@ -11,6 +12,10 @@ import org.bukkit.inventory.ItemStack
class FarmerMinionType : MinionType("farmer", AxMinionsPlugin.INSTANCE.getResource("minions/farmer.yml")!!) {
override fun shouldRun(minion: Minion): Boolean {
return MinionTicker.getTick() % minion.getNextAction() == 0L
}
override fun run(minion: Minion) {
LocationUtils.getAllBlocksInRadius(minion.getLocation(), minion.getRange(), false).fastFor { location ->
val block = location.block

View File

@ -9,6 +9,7 @@ inline fun <T> Array<T>.fastFor(action: (T) -> Unit) {
inline fun <T> List<T>.fastFor(action: (T) -> Unit) {
val indices = indices
if (indices.last == 0) return
for (i in indices) {
action(get(i))
}

View File

@ -17,12 +17,13 @@ object LocationUtils {
val rangeZ = (blockZ - radius).rangeTo((blockZ + radius)).step(1.0)
val radiusSquared = radius * radius
val smallRadiusSquared = (radius - 1) * (radius -1)
val smallRadiusSquared = (radius - 1) * (radius - 1)
for (x in rangeX) {
for (y in rangeY) {
for (z in rangeZ) {
val distance = ((blockX - x) * (blockX - x) + ((blockZ - z) * (blockZ - z)) + ((blockY - y) * (blockY - y)))
val distance =
((blockX - x) * (blockX - x) + ((blockZ - z) * (blockZ - z)) + ((blockY - y) * (blockY - y)))
if (distance < radiusSquared && !(filterEmpty && distance < smallRadiusSquared)) {
blocks.add(Location(location.world, x, y, z))

Binary file not shown.