Improve (ParcelTarget)ing for commands, ParcelOwner things, various little bits
This commit is contained in:
@@ -5,7 +5,7 @@ import org.bukkit.OfflinePlayer
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface AddedData {
|
interface AddedData {
|
||||||
val added: Map<UUID, AddedStatus>
|
val addedMap: Map<UUID, AddedStatus>
|
||||||
|
|
||||||
fun getAddedStatus(uuid: UUID): AddedStatus
|
fun getAddedStatus(uuid: UUID): AddedStatus
|
||||||
fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
|
fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
|
||||||
@@ -28,12 +28,12 @@ interface AddedData {
|
|||||||
fun unban(player: OfflinePlayer) = unban(player.uuid)
|
fun unban(player: OfflinePlayer) = unban(player.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
open class AddedDataHolder(override var added: MutableMap<UUID, AddedStatus>
|
open class AddedDataHolder(override var addedMap: MutableMap<UUID, AddedStatus>
|
||||||
= mutableMapOf<UUID, AddedStatus>()) : AddedData {
|
= mutableMapOf<UUID, AddedStatus>()) : AddedData {
|
||||||
override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
override fun getAddedStatus(uuid: UUID): AddedStatus = addedMap.getOrDefault(uuid, AddedStatus.DEFAULT)
|
||||||
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
||||||
?.let { added.put(uuid, it) != it }
|
?.let { addedMap.put(uuid, it) != it }
|
||||||
?: added.remove(uuid) != null
|
?: addedMap.remove(uuid) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AddedStatus {
|
enum class AddedStatus {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataMan
|
|||||||
data: MutableMap<UUID, AddedStatus> = emptyData)
|
data: MutableMap<UUID, AddedStatus> = emptyData)
|
||||||
: AddedDataHolder(data), GlobalAddedData {
|
: AddedDataHolder(data), GlobalAddedData {
|
||||||
|
|
||||||
private inline var data get() = added; set(value) = run { added = value }
|
private inline var data get() = addedMap; set(value) = run { addedMap = value }
|
||||||
private inline val isEmpty get() = data === emptyData
|
private inline val isEmpty get() = data === emptyData
|
||||||
|
|
||||||
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
|
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
|
||||||
|
|||||||
@@ -1,26 +1,14 @@
|
|||||||
package io.dico.parcels2
|
package io.dico.parcels2
|
||||||
|
|
||||||
|
import io.dico.dicore.Formatting
|
||||||
import io.dico.parcels2.util.Vec2i
|
import io.dico.parcels2.util.Vec2i
|
||||||
|
import io.dico.parcels2.util.getPlayerName
|
||||||
import io.dico.parcels2.util.hasBuildAnywhere
|
import io.dico.parcels2.util.hasBuildAnywhere
|
||||||
import org.bukkit.Bukkit
|
|
||||||
import org.bukkit.OfflinePlayer
|
import org.bukkit.OfflinePlayer
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
interface ParcelData : AddedData {
|
|
||||||
var owner: ParcelOwner?
|
|
||||||
val since: DateTime?
|
|
||||||
|
|
||||||
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
|
|
||||||
|
|
||||||
var allowInteractInputs: Boolean
|
|
||||||
var allowInteractInventory: Boolean
|
|
||||||
|
|
||||||
fun isOwner(uuid: UUID): Boolean {
|
|
||||||
return owner?.uuid == uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parcel implementation of ParcelData will update the database when changes are made.
|
* Parcel implementation of ParcelData will update the database when changes are made.
|
||||||
@@ -31,16 +19,12 @@ interface ParcelData : AddedData {
|
|||||||
* Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
|
* Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
|
||||||
*/
|
*/
|
||||||
class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
||||||
|
var data: ParcelData = ParcelDataHolder(); private set
|
||||||
|
|
||||||
val id get() = "${pos.x}:${pos.z}"
|
val id get() = "${pos.x}:${pos.z}"
|
||||||
val homeLocation get() = world.generator.getHomeLocation(this)
|
val homeLocation get() = world.generator.getHomeLocation(this)
|
||||||
private var blockVisitors = 0
|
|
||||||
|
|
||||||
val infoString: String
|
val infoString by ParcelInfoStringComputer
|
||||||
get() {
|
|
||||||
return "$id; owned by ${owner?.let { it.name ?: Bukkit.getOfflinePlayer(it.uuid).name }}"
|
|
||||||
}
|
|
||||||
|
|
||||||
var data: ParcelData = ParcelDataHolder(); private set
|
|
||||||
|
|
||||||
fun copyDataIgnoringDatabase(data: ParcelData) {
|
fun copyDataIgnoringDatabase(data: ParcelData) {
|
||||||
this.data = data
|
this.data = data
|
||||||
@@ -48,14 +32,19 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
|||||||
|
|
||||||
fun copyData(data: ParcelData) {
|
fun copyData(data: ParcelData) {
|
||||||
world.storage.setParcelData(this, data)
|
world.storage.setParcelData(this, data)
|
||||||
this.data = data
|
copyDataIgnoringDatabase(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val added: Map<UUID, AddedStatus> get() = data.added
|
override val addedMap: Map<UUID, AddedStatus> get() = data.addedMap
|
||||||
override fun getAddedStatus(uuid: UUID) = data.getAddedStatus(uuid)
|
override fun getAddedStatus(uuid: UUID) = data.getAddedStatus(uuid)
|
||||||
override fun isBanned(uuid: UUID) = data.isBanned(uuid)
|
override fun isBanned(uuid: UUID) = data.isBanned(uuid)
|
||||||
override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
|
override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
|
||||||
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = data.canBuild(player)
|
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean): Boolean {
|
||||||
|
return (data.canBuild(player, checkAdmin, false))
|
||||||
|
|| checkGlobal && world.globalAddedData[owner ?: return false].isAllowed(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
val globalAddedMap: Map<UUID, AddedStatus>? get() = owner?.let { world.globalAddedData[it].addedMap }
|
||||||
|
|
||||||
override val since: DateTime? get() = data.since
|
override val since: DateTime? get() = data.since
|
||||||
|
|
||||||
@@ -93,7 +82,22 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
|||||||
var hasBlockVisitors: Boolean = false; private set
|
var hasBlockVisitors: Boolean = false; private set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ParcelData : AddedData {
|
||||||
|
var owner: ParcelOwner?
|
||||||
|
val since: DateTime?
|
||||||
|
|
||||||
|
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
|
||||||
|
|
||||||
|
var allowInteractInputs: Boolean
|
||||||
|
var allowInteractInventory: Boolean
|
||||||
|
|
||||||
|
fun isOwner(uuid: UUID): Boolean {
|
||||||
|
return owner?.uuid == uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
||||||
|
|
||||||
override var owner: ParcelOwner? = null
|
override var owner: ParcelOwner? = null
|
||||||
override var since: DateTime? = null
|
override var since: DateTime? = null
|
||||||
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.uniqueId)
|
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.uniqueId)
|
||||||
@@ -104,3 +108,59 @@ class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
|||||||
override var allowInteractInventory = true
|
override var allowInteractInventory = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object ParcelInfoStringComputer {
|
||||||
|
val infoStringColor1 = Formatting.GREEN
|
||||||
|
val infoStringColor2 = Formatting.AQUA
|
||||||
|
|
||||||
|
private inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
|
||||||
|
append(infoStringColor1)
|
||||||
|
append(name)
|
||||||
|
append(": ")
|
||||||
|
append(infoStringColor2)
|
||||||
|
value()
|
||||||
|
append(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(parcel: Parcel, property: KProperty<*>): String = buildString {
|
||||||
|
appendField("ID") {
|
||||||
|
append(parcel.pos.x)
|
||||||
|
append(':')
|
||||||
|
append(parcel.pos.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
appendField("Owner") {
|
||||||
|
val owner = parcel.owner
|
||||||
|
if (owner == null) {
|
||||||
|
append(infoStringColor1)
|
||||||
|
append("none")
|
||||||
|
} else {
|
||||||
|
append(owner.notNullName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// plotme appends biome here
|
||||||
|
|
||||||
|
append('\n')
|
||||||
|
|
||||||
|
val allowedMap = parcel.addedMap.filterValues { it.isAllowed }
|
||||||
|
if (allowedMap.isNotEmpty()) appendField("Allowed") {
|
||||||
|
allowedMap.keys.map(::getPlayerName).joinTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bannedMap = parcel.addedMap.filterValues { it.isBanned }
|
||||||
|
if (bannedMap.isNotEmpty()) appendField("Banned") {
|
||||||
|
bannedMap.keys.map(::getPlayerName).joinTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parcel.allowInteractInputs || !parcel.allowInteractInventory) {
|
||||||
|
appendField("Options") {
|
||||||
|
append("(")
|
||||||
|
appendField("inputs") { append(parcel.allowInteractInputs)}
|
||||||
|
append(", ")
|
||||||
|
appendField("inventory") { append(parcel.allowInteractInventory) }
|
||||||
|
append(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package io.dico.parcels2
|
package io.dico.parcels2
|
||||||
|
|
||||||
import io.dico.parcels2.util.getPlayerNameOrDefault
|
import io.dico.parcels2.util.getPlayerNameOrDefault
|
||||||
@@ -10,10 +12,8 @@ import java.util.*
|
|||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
class ParcelOwner private constructor(val uuid: UUID?,
|
class ParcelOwner private constructor(val uuid: UUID?,
|
||||||
name: String?) {
|
val name: String?) {
|
||||||
var name: String? = name
|
val notNullName: String by lazy { name ?: getPlayerNameOrDefault(uuid!!) }
|
||||||
get() = field ?: getPlayerNameOrDefault(uuid!!).also { field = it }
|
|
||||||
private set
|
|
||||||
|
|
||||||
constructor(name: String) : this(null, name)
|
constructor(name: String) : this(null, name)
|
||||||
constructor(uuid: UUID) : this(uuid, null)
|
constructor(uuid: UUID) : this(uuid, null)
|
||||||
@@ -24,13 +24,13 @@ class ParcelOwner private constructor(val uuid: UUID?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline val hasUUID: Boolean get() = uuid != null
|
inline val hasUUID: Boolean get() = uuid != null
|
||||||
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
|
||||||
val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayer(name) }
|
|
||||||
|
|
||||||
|
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
val offlinePlayer
|
val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayerExact(name) }
|
||||||
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } }
|
||||||
?.takeIf { it.isValid }
|
@Suppress("DEPRECATION")
|
||||||
|
val offlinePlayerAllowingNameMatch: OfflinePlayer? get() = offlinePlayer ?: Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
|
||||||
|
|
||||||
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
|
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
|
||||||
return uuid?.let { it == player.uniqueId } ?: false
|
return uuid?.let { it == player.uniqueId } ?: false
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ class Worlds(val plugin: ParcelsPlugin) {
|
|||||||
worldName,
|
worldName,
|
||||||
worldOptions,
|
worldOptions,
|
||||||
worldOptions.generator.getGenerator(this, worldName),
|
worldOptions.generator.getGenerator(this, worldName),
|
||||||
plugin.storage)
|
plugin.storage,
|
||||||
|
plugin.globalAddedData)
|
||||||
|
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
@@ -117,7 +118,8 @@ interface ParcelProvider {
|
|||||||
class ParcelWorld constructor(val name: String,
|
class ParcelWorld constructor(val name: String,
|
||||||
val options: WorldOptions,
|
val options: WorldOptions,
|
||||||
val generator: ParcelGenerator,
|
val generator: ParcelGenerator,
|
||||||
val storage: Storage) : ParcelProvider by generator, ParcelContainer {
|
val storage: Storage,
|
||||||
|
val globalAddedData: GlobalAddedDataManager) : ParcelProvider by generator, ParcelContainer {
|
||||||
val world: World by lazy {
|
val world: World by lazy {
|
||||||
Bukkit.getWorld(name) ?: throw NullPointerException("World $name does not appear to be loaded")
|
Bukkit.getWorld(name) ?: throw NullPointerException("World $name does not appear to be loaded")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider {
|
|||||||
|
|
||||||
abstract val factory: GeneratorFactory
|
abstract val factory: GeneratorFactory
|
||||||
|
|
||||||
|
abstract fun parcelIDAt(x: Int, z: Int): Vec2i?
|
||||||
|
|
||||||
abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
|
abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
|
||||||
|
|
||||||
abstract fun populate(world: World?, random: Random?, chunk: Chunk?)
|
abstract fun populate(world: World?, random: Random?, chunk: Chunk?)
|
||||||
@@ -171,19 +173,29 @@ class DefaultParcelGenerator(val worlds: Worlds, val name: String, private val o
|
|||||||
return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
|
return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun parcelAt(x: Int, z: Int): Parcel? {
|
private inline fun <T> convertBlockLocationToID(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
|
||||||
val sectionSize = sectionSize
|
val sectionSize = sectionSize
|
||||||
val parcelSize = o.parcelSize
|
val parcelSize = o.parcelSize
|
||||||
val absX = x - o.offsetX - pathOffset
|
val absX = x - o.offsetX - pathOffset
|
||||||
val absZ = z - o.offsetZ - pathOffset
|
val absZ = z - o.offsetZ - pathOffset
|
||||||
val modX = absX umod sectionSize
|
val modX = absX umod sectionSize
|
||||||
val modZ = absZ umod sectionSize
|
val modZ = absZ umod sectionSize
|
||||||
if (0 <= modX && modX < parcelSize && 0 <= modZ && modZ < parcelSize) {
|
if (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
|
||||||
return world.parcelByID((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
|
return mapper((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun parcelIDAt(x: Int, z: Int): Vec2i? {
|
||||||
|
return convertBlockLocationToID(x, z) { idx, idz -> Vec2i(idx, idz) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parcelAt(x: Int, z: Int): Parcel? {
|
||||||
|
return convertBlockLocationToID(x, z) { idx, idz ->
|
||||||
|
world.parcelByID(idx, idz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX,
|
override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX,
|
||||||
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
|
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import io.dico.dicore.command.annotation.RequireParameters
|
|||||||
import io.dico.parcels2.ParcelOwner
|
import io.dico.parcels2.ParcelOwner
|
||||||
import io.dico.parcels2.ParcelsPlugin
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
import io.dico.parcels2.blockvisitor.RegionTraversal
|
import io.dico.parcels2.blockvisitor.RegionTraversal
|
||||||
import io.dico.parcels2.command.NamedParcelDefaultValue.FIRST_OWNED
|
|
||||||
import io.dico.parcels2.storage.getParcelBySerializedValue
|
import io.dico.parcels2.storage.getParcelBySerializedValue
|
||||||
import io.dico.parcels2.util.hasAdminManage
|
import io.dico.parcels2.util.hasAdminManage
|
||||||
import io.dico.parcels2.util.hasParcelHomeOthers
|
import io.dico.parcels2.util.hasParcelHomeOthers
|
||||||
@@ -49,18 +48,17 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
"more than one parcel",
|
"more than one parcel",
|
||||||
shortVersion = "teleports you to parcels")
|
shortVersion = "teleports you to parcels")
|
||||||
@RequireParameters(0)
|
@RequireParameters(0)
|
||||||
suspend fun cmdHome(player: Player,
|
suspend fun cmdHome(player: Player, @ParcelTarget.Kind(ParcelTarget.OWNER_REAL) target: ParcelTarget): Any? {
|
||||||
@NamedParcelDefault(FIRST_OWNED) target: NamedParcelTarget): Any? {
|
val ownerTarget = target as ParcelTarget.ByOwner
|
||||||
if (player !== target.player && !player.hasParcelHomeOthers) {
|
if (!ownerTarget.owner.matches(player) && !player.hasParcelHomeOthers) {
|
||||||
error("You do not have permission to teleport to other people's parcels")
|
error("You do not have permission to teleport to other people's parcels")
|
||||||
}
|
}
|
||||||
|
|
||||||
val ownedParcelsResult = plugin.storage.getOwnedParcels(ParcelOwner(uuid = target.player.uuid)).await()
|
val ownedParcelsResult = plugin.storage.getOwnedParcels(ownerTarget.owner).await()
|
||||||
|
|
||||||
val uuid = target.player.uuid
|
|
||||||
val ownedParcels = ownedParcelsResult
|
val ownedParcels = ownedParcelsResult
|
||||||
.map { worlds.getParcelBySerializedValue(it) }
|
.map { worlds.getParcelBySerializedValue(it) }
|
||||||
.filter { it != null && it.world == target.world && it.owner?.uuid == uuid }
|
.filter { it != null && ownerTarget.world == it.world && ownerTarget.owner == it.owner }
|
||||||
|
|
||||||
val targetMatch = ownedParcels.getOrNull(target.index)
|
val targetMatch = ownedParcels.getOrNull(target.index)
|
||||||
?: error("The specified parcel could not be matched")
|
?: error("The specified parcel could not be matched")
|
||||||
@@ -79,7 +77,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkParcelLimit(player)
|
checkParcelLimit(player)
|
||||||
parcel.owner = ParcelOwner(uuid = player.uuid, name = player.name)
|
parcel.owner = ParcelOwner(player)
|
||||||
return "Enjoy your new parcel!"
|
return "Enjoy your new parcel!"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +98,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
|
|
||||||
@Cmd("swap")
|
@Cmd("swap")
|
||||||
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||||
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cmd("make_mess")
|
@Cmd("make_mess")
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package io.dico.parcels2.command
|
|
||||||
|
|
||||||
import io.dico.parcels2.Parcel
|
|
||||||
import io.dico.parcels2.ParcelWorld
|
|
||||||
import io.dico.parcels2.ParcelsPlugin
|
|
||||||
import kotlinx.coroutines.experimental.Deferred
|
|
||||||
|
|
||||||
interface ParcelTarget {
|
|
||||||
val world: ParcelWorld
|
|
||||||
val isByID: Boolean
|
|
||||||
val isByOwner: Boolean get() = !isByID
|
|
||||||
suspend fun ParcelsPlugin.await(): Parcel?
|
|
||||||
fun ParcelsPlugin.get(): Deferred<Parcel?> =
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParcelTargetByOwner : ParcelTarget {
|
|
||||||
override val isByID get() = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParcelTargetByID : ParcelTarget {
|
|
||||||
override val isByID get() = true
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -49,75 +49,3 @@ class ParcelParameterType(val worlds: Worlds) : ParameterType<Parcel, Void>(Parc
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NamedParcelTarget(val world: ParcelWorld, val player: OfflinePlayer, val index: Int)
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
annotation class NamedParcelDefault(val value: NamedParcelDefaultValue)
|
|
||||||
|
|
||||||
enum class NamedParcelDefaultValue {
|
|
||||||
FIRST_OWNED,
|
|
||||||
NULL
|
|
||||||
}
|
|
||||||
|
|
||||||
class NamedParcelTargetConfig : ParameterConfig<NamedParcelDefault,
|
|
||||||
NamedParcelDefaultValue>(NamedParcelDefault::class.java) {
|
|
||||||
|
|
||||||
override fun toParameterInfo(annotation: NamedParcelDefault): NamedParcelDefaultValue {
|
|
||||||
return annotation.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParcelHomeParameterType(val worlds: Worlds) : ParameterType<NamedParcelTarget,
|
|
||||||
NamedParcelDefaultValue>(NamedParcelTarget::class.java, NamedParcelTargetConfig()) {
|
|
||||||
|
|
||||||
val regex = Regex.fromLiteral("((.+)->)?(.+)|((.+):([0-9]+))")
|
|
||||||
|
|
||||||
private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>): Player {
|
|
||||||
if (sender !is Player) invalidInput(parameter, "console cannot omit the player name")
|
|
||||||
return sender
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
|
||||||
private fun getOfflinePlayer(input: String, parameter: Parameter<*, *>) = Bukkit.getOfflinePlayer(input)
|
|
||||||
?.takeIf { it.isValid }
|
|
||||||
?: invalidInput(parameter, "do not know who $input is")
|
|
||||||
|
|
||||||
override fun parse(parameter: Parameter<NamedParcelTarget, NamedParcelDefaultValue>,
|
|
||||||
sender: CommandSender, buffer: ArgumentBuffer): NamedParcelTarget {
|
|
||||||
val matchResult = regex.matchEntire(buffer.next())
|
|
||||||
?: invalidInput(parameter, "must be a player, index, or player:index (/${regex.pattern}/)")
|
|
||||||
|
|
||||||
val world = worlds.getTargetWorld(matchResult.groupValues[2], sender, parameter)
|
|
||||||
|
|
||||||
matchResult.groupValues[3].takeUnless { it.isEmpty() }?.let {
|
|
||||||
// first group was matched, it's a player or an int
|
|
||||||
it.toIntOrNull()?.let {
|
|
||||||
requirePlayer(sender, parameter)
|
|
||||||
return NamedParcelTarget(world, sender as Player, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NamedParcelTarget(world, getOfflinePlayer(it, parameter), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
val player = getOfflinePlayer(matchResult.groupValues[5], parameter)
|
|
||||||
val index = matchResult.groupValues[6].toIntOrNull()
|
|
||||||
?: invalidInput(parameter, "couldn't parse int")
|
|
||||||
|
|
||||||
return NamedParcelTarget(world, player, index)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDefaultValue(parameter: Parameter<NamedParcelTarget, NamedParcelDefaultValue>,
|
|
||||||
sender: CommandSender, buffer: ArgumentBuffer): NamedParcelTarget? {
|
|
||||||
if (parameter.paramInfo == NamedParcelDefaultValue.NULL) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val world = worlds.getTargetWorld(null, sender, parameter)
|
|
||||||
val player = requirePlayer(sender, parameter)
|
|
||||||
return NamedParcelTarget(world, player, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
163
src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
Normal file
163
src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.dicore.command.parameter.ArgumentBuffer
|
||||||
|
import io.dico.dicore.command.parameter.Parameter
|
||||||
|
import io.dico.dicore.command.parameter.type.ParameterConfig
|
||||||
|
import io.dico.dicore.command.parameter.type.ParameterType
|
||||||
|
import io.dico.parcels2.*
|
||||||
|
import io.dico.parcels2.storage.getParcelBySerializedValue
|
||||||
|
import io.dico.parcels2.util.Vec2i
|
||||||
|
import io.dico.parcels2.util.floor
|
||||||
|
import io.dico.parcels2.util.isValid
|
||||||
|
import kotlinx.coroutines.experimental.Deferred
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||||
|
abstract suspend fun ParcelsPlugin.getParcelSuspend(): Parcel?
|
||||||
|
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend() }
|
||||||
|
|
||||||
|
class ByID(world: ParcelWorld, val id: Vec2i?, isDefault: Boolean) : ParcelTarget(world, isDefault) {
|
||||||
|
override suspend fun ParcelsPlugin.getParcelSuspend(): Parcel? = getParcel()
|
||||||
|
fun getParcel() = id?.let { world.parcelByID(it) }
|
||||||
|
val isPath: Boolean get() = id == null
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByOwner(world: ParcelWorld, val owner: ParcelOwner, val index: Int, isDefault: Boolean) : ParcelTarget(world, isDefault) {
|
||||||
|
init {
|
||||||
|
if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun ParcelsPlugin.getParcelSuspend(): Parcel? {
|
||||||
|
val ownedParcelsSerialized = storage.getOwnedParcels(owner).await()
|
||||||
|
val ownedParcels = ownedParcelsSerialized
|
||||||
|
.map { worlds.getParcelBySerializedValue(it) }
|
||||||
|
.filter { it != null && world == it.world && owner == it.owner }
|
||||||
|
return ownedParcels.getOrNull(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
annotation class Kind(val kind: Int)
|
||||||
|
|
||||||
|
companion object Config : ParameterConfig<Kind, Int>(Kind::class.java) {
|
||||||
|
override fun toParameterInfo(annotation: Kind): Int {
|
||||||
|
return annotation.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
const val ID = 1 // ID
|
||||||
|
const val OWNER_REAL = 2 // an owner backed by a UUID
|
||||||
|
const val OWNER_FAKE = 3 // an owner not backed by a UUID
|
||||||
|
|
||||||
|
const val OWNER = OWNER_REAL or OWNER_FAKE // any owner
|
||||||
|
const val ANY = ID or OWNER_REAL or OWNER_FAKE // any
|
||||||
|
const val REAL = ID or OWNER_REAL // no owner not backed by a UUID
|
||||||
|
|
||||||
|
const val DEFAULT_KIND = REAL
|
||||||
|
|
||||||
|
const val PREFER_OWNED_FOR_DEFAULT = 4 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
|
||||||
|
// instead of parcel that the player is in
|
||||||
|
}
|
||||||
|
|
||||||
|
class PType(val worlds: Worlds) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, ParcelTarget.Config) {
|
||||||
|
|
||||||
|
override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
|
||||||
|
var input = buffer.next()
|
||||||
|
val worldString = input.substringBefore("->", missingDelimiterValue = "")
|
||||||
|
input = input.substringAfter("->")
|
||||||
|
|
||||||
|
val world = if (worldString.isEmpty()) {
|
||||||
|
val player = requirePlayer(sender, parameter, "the world")
|
||||||
|
worlds.getWorld(player.world)
|
||||||
|
?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world")
|
||||||
|
} else {
|
||||||
|
worlds.getWorld(worldString) ?: invalidInput(parameter, "$worldString is not a parcel world")
|
||||||
|
}
|
||||||
|
|
||||||
|
val kind = parameter.paramInfo ?: DEFAULT_KIND
|
||||||
|
if (input.contains(',')) {
|
||||||
|
if (kind and ID == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
|
||||||
|
return ByID(world, getId(parameter, input), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by OWNER, that is, an owner and index")
|
||||||
|
val (owner, index) = getHomeIndex(parameter, sender, input)
|
||||||
|
return ByOwner(world, owner, index, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
|
||||||
|
val x = input.substringBefore(',').run {
|
||||||
|
toIntOrNull() ?: invalidInput(parameter, "ID(x) must be an integer, $this is not an integer")
|
||||||
|
}
|
||||||
|
val z = input.substringAfter(',').run {
|
||||||
|
toIntOrNull() ?: invalidInput(parameter, "ID(z) must be an integer, $this is not an integer")
|
||||||
|
}
|
||||||
|
return Vec2i(x, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHomeIndex(parameter: Parameter<*, Int>, sender: CommandSender, input: String): Pair<ParcelOwner, Int> {
|
||||||
|
val splitIdx = input.indexOf(':')
|
||||||
|
val ownerString: String
|
||||||
|
val indexString: String
|
||||||
|
|
||||||
|
if (splitIdx == -1) {
|
||||||
|
// just the index.
|
||||||
|
ownerString = ""
|
||||||
|
indexString = input
|
||||||
|
} else {
|
||||||
|
ownerString = input.substring(0, splitIdx)
|
||||||
|
indexString = input.substring(0, splitIdx + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val owner = if (ownerString.isEmpty())
|
||||||
|
ParcelOwner(requirePlayer(sender, parameter, "the player"))
|
||||||
|
else
|
||||||
|
inputAsOwner(parameter, ownerString)
|
||||||
|
|
||||||
|
val index = if (indexString.isEmpty()) 0 else indexString.toIntOrNull()
|
||||||
|
?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
|
||||||
|
|
||||||
|
return owner to index
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player {
|
||||||
|
if (sender !is Player) invalidInput(parameter, "console cannot omit the $objName")
|
||||||
|
return sender
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun inputAsOwner(parameter: Parameter<*, Int>, input: String): ParcelOwner {
|
||||||
|
val kind = parameter.paramInfo ?: DEFAULT_KIND
|
||||||
|
if (kind and OWNER_REAL == 0) {
|
||||||
|
return ParcelOwner(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
val player = Bukkit.getOfflinePlayer(input).takeIf { it.isValid }
|
||||||
|
if (player == null) {
|
||||||
|
if (kind and OWNER_FAKE == 0) invalidInput(parameter, "The player $input does not exist")
|
||||||
|
return ParcelOwner(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParcelOwner(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? {
|
||||||
|
val kind = parameter.paramInfo ?: DEFAULT_KIND
|
||||||
|
val useLocation = when {
|
||||||
|
kind and REAL == REAL -> kind and PREFER_OWNED_FOR_DEFAULT == 0
|
||||||
|
kind and ID != 0 -> true
|
||||||
|
kind and OWNER_REAL != 0 -> false
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val player = requirePlayer(sender, parameter, "the parcel")
|
||||||
|
val world = worlds.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel")
|
||||||
|
if (useLocation) {
|
||||||
|
val id = player.location.let { world.generator.parcelIDAt(it.x.floor(), it.z.floor()) }
|
||||||
|
return ByID(world, id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ByOwner(world, ParcelOwner(player), 0, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,7 +107,7 @@ class ExposedBacking(private val dataSourceFactory: suspend () -> DataSource) :
|
|||||||
|
|
||||||
setParcelOwner(parcelFor, data.owner)
|
setParcelOwner(parcelFor, data.owner)
|
||||||
|
|
||||||
for ((uuid, status) in data.added) {
|
for ((uuid, status) in data.addedMap) {
|
||||||
setLocalPlayerStatus(parcelFor, uuid, status)
|
setLocalPlayerStatus(parcelFor, uuid, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ class ExposedBacking(private val dataSourceFactory: suspend () -> DataSource) :
|
|||||||
since = row[ParcelsT.claim_time]
|
since = row[ParcelsT.claim_time]
|
||||||
|
|
||||||
val parcelId = row[ParcelsT.id]
|
val parcelId = row[ParcelsT.id]
|
||||||
added = AddedLocalT.readAddedData(parcelId)
|
addedMap = AddedLocalT.readAddedData(parcelId)
|
||||||
|
|
||||||
AddedLocalT.select { AddedLocalT.attach_id eq parcelId }.forEach {
|
AddedLocalT.select { AddedLocalT.attach_id eq parcelId }.forEach {
|
||||||
val uuid = it[AddedLocalT.player_uuid].toUUID()
|
val uuid = it[AddedLocalT.player_uuid].toUUID()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj,
|
|||||||
return table.insert(body)[id] ?: insertError(objName)
|
return table.insert(body)[id] ?: insertError(objName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun insertError(obj: String): Nothing = throw ExposedDatabaseException("This should not happen - failed to insert $obj and get its id")
|
private inline fun insertError(obj: String): Nothing = throw ExposedDatabaseException("This should not happen - failed to insert $obj and getParcelDeferred its id")
|
||||||
|
|
||||||
abstract fun getId(obj: QueryObj): Int?
|
abstract fun getId(obj: QueryObj): Int?
|
||||||
abstract fun getOrInitId(obj: QueryObj): Int
|
abstract fun getOrInitId(obj: QueryObj): Int
|
||||||
@@ -91,31 +91,32 @@ object ParcelsT : IdTransactionsTable<ParcelsT, Parcel, SerializableParcel>("par
|
|||||||
|
|
||||||
object OwnersT : IdTransactionsTable<OwnersT, ParcelOwner, ParcelOwner>("parcel_owners", "owner_id") {
|
object OwnersT : IdTransactionsTable<OwnersT, ParcelOwner, ParcelOwner>("parcel_owners", "owner_id") {
|
||||||
val uuid = binary("uuid", 2).nullable()
|
val uuid = binary("uuid", 2).nullable()
|
||||||
val name = varchar("name", 32).nullable()
|
val name = varchar("name", 32)
|
||||||
val index_pair = uniqueIndexR("index_pair", uuid, name)
|
val index_pair = uniqueIndexR("index_pair", uuid, name)
|
||||||
|
|
||||||
private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
|
private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
|
||||||
private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
|
private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
|
||||||
private inline fun getId(name: String) = getId { OwnersT.name eq name }
|
private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name eq nameIn) }
|
||||||
|
|
||||||
private inline fun getOrInitId(uuid: UUID) = uuid.toByteArray().let { binaryUuid ->
|
private inline fun getOrInitId(uuid: UUID, name: String) = uuid.toByteArray().let { binaryUuid ->
|
||||||
getId(binaryUuid)
|
getId(binaryUuid) ?: insertAndGetId("owner(uuid = $uuid)") {
|
||||||
?: insertAndGetId("owner(uuid = $uuid)") { it[OwnersT.uuid] = binaryUuid }
|
it[this@OwnersT.uuid] = binaryUuid
|
||||||
|
it[this@OwnersT.name] = name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun getOrInitId(name: String) =
|
private inline fun getOrInitId(name: String) =
|
||||||
getId(name)
|
getId(name) ?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
|
||||||
?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
|
|
||||||
|
|
||||||
override fun getId(owner: ParcelOwner): Int? =
|
override fun getId(owner: ParcelOwner): Int? =
|
||||||
if (owner.hasUUID) getId(owner.uuid!!)
|
if (owner.hasUUID) getId(owner.uuid!!)
|
||||||
else getId(owner.name!!)
|
else getId(owner.name!!)
|
||||||
|
|
||||||
override fun getOrInitId(owner: ParcelOwner): Int =
|
override fun getOrInitId(owner: ParcelOwner): Int =
|
||||||
if (owner.hasUUID) getOrInitId(owner.uuid!!)
|
if (owner.hasUUID) getOrInitId(owner.uuid!!, owner.notNullName)
|
||||||
else getOrInitId(owner.name!!)
|
else getOrInitId(owner.name!!)
|
||||||
|
|
||||||
override fun getSerializable(row: ResultRow): ParcelOwner {
|
override fun getSerializable(row: ResultRow): ParcelOwner {
|
||||||
return row[uuid]?.toUUID()?.let { ParcelOwner(it) } ?: ParcelOwner(row[name]!!)
|
return row[uuid]?.toUUID()?.let { ParcelOwner(it) } ?: ParcelOwner(row[name])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ object AddedGlobalT : AddedTable<ParcelOwner, ParcelOwner>("parcels_added_global
|
|||||||
|
|
||||||
object ParcelOptionsT : Table("parcel_options") {
|
object ParcelOptionsT : Table("parcel_options") {
|
||||||
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
|
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
|
||||||
val interact_inventory = bool("interact_inventory").default(false)
|
val interact_inventory = bool("interact_inventory").default(true)
|
||||||
val interact_inputs = bool("interact_inputs").default(false)
|
val interact_inputs = bool("interact_inputs").default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableMap<UUID, AddedStatus>>>
|
typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableMap<UUID, AddedStatus>>>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ data class Vec3i(
|
|||||||
inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
|
inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
inline class IVec3i(private val data: Long) {
|
private /*inline */class IVec3i(private val data: Long) {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val mask = 0x001F_FFFF
|
const val mask = 0x001F_FFFF
|
||||||
@@ -34,7 +34,8 @@ inline class IVec3i(private val data: Long) {
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun Long.extractInt(offset: Int): Int {
|
inline fun Long.extractInt(offset: Int): Int {
|
||||||
return ushr(offset).toInt().and(mask)
|
val result = ushr(offset).toInt().and(mask)
|
||||||
|
return if (result > max) result or mask.inv() else result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user