summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico Karssiens <dico.karssiens@gmail.com>2018-08-02 01:16:38 +0100
committerDico Karssiens <dico.karssiens@gmail.com>2018-08-02 01:16:38 +0100
commit3917855a72c60d1c78632949b4fea21471873347 (patch)
tree4dcdbd770b1a74cc307adc0c5a020617d4233351
parent472e700e0422d1829aa26e04b74e2077807e75f0 (diff)
Improve (ParcelTarget)ing for commands, ParcelOwner things, various little bits
-rw-r--r--src/main/kotlin/io/dico/parcels2/AddedData.kt10
-rw-r--r--src/main/kotlin/io/dico/parcels2/GlobalAddedData.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt110
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelOwner.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/WorldGenerator.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt16
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt23
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt72
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt163
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt21
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/Vec3i.kt5
14 files changed, 307 insertions, 165 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/AddedData.kt b/src/main/kotlin/io/dico/parcels2/AddedData.kt
index 7bcf8f1..5d2a68d 100644
--- a/src/main/kotlin/io/dico/parcels2/AddedData.kt
+++ b/src/main/kotlin/io/dico/parcels2/AddedData.kt
@@ -5,7 +5,7 @@ import org.bukkit.OfflinePlayer
import java.util.*
interface AddedData {
- val added: Map<UUID, AddedStatus>
+ val addedMap: Map<UUID, AddedStatus>
fun getAddedStatus(uuid: UUID): AddedStatus
fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
@@ -28,12 +28,12 @@ interface AddedData {
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 {
- 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 }
- ?.let { added.put(uuid, it) != it }
- ?: added.remove(uuid) != null
+ ?.let { addedMap.put(uuid, it) != it }
+ ?: addedMap.remove(uuid) != null
}
enum class AddedStatus {
diff --git a/src/main/kotlin/io/dico/parcels2/GlobalAddedData.kt b/src/main/kotlin/io/dico/parcels2/GlobalAddedData.kt
index 055e681..69bc07f 100644
--- a/src/main/kotlin/io/dico/parcels2/GlobalAddedData.kt
+++ b/src/main/kotlin/io/dico/parcels2/GlobalAddedData.kt
@@ -23,7 +23,7 @@ class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataMan
data: MutableMap<UUID, AddedStatus> = emptyData)
: 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
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index c8e7713..712c32f 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -1,26 +1,14 @@
package io.dico.parcels2
+import io.dico.dicore.Formatting
import io.dico.parcels2.util.Vec2i
+import io.dico.parcels2.util.getPlayerName
import io.dico.parcels2.util.hasBuildAnywhere
-import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.joda.time.DateTime
import java.util.*
-
-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
- }
-}
+import kotlin.reflect.KProperty
/**
* 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.
*/
class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
+ var data: ParcelData = ParcelDataHolder(); private set
+
val id get() = "${pos.x}:${pos.z}"
val homeLocation get() = world.generator.getHomeLocation(this)
- private var blockVisitors = 0
- val infoString: String
- get() {
- return "$id; owned by ${owner?.let { it.name ?: Bukkit.getOfflinePlayer(it.uuid).name }}"
- }
-
- var data: ParcelData = ParcelDataHolder(); private set
+ val infoString by ParcelInfoStringComputer
fun copyDataIgnoringDatabase(data: ParcelData) {
this.data = data
@@ -48,14 +32,19 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
fun copyData(data: ParcelData) {
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 isBanned(uuid: UUID) = data.isBanned(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
@@ -93,7 +82,22 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
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 {
+
override var owner: ParcelOwner? = null
override var since: DateTime? = null
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.uniqueId)
@@ -104,3 +108,59 @@ class ParcelDataHolder : AddedDataHolder(), ParcelData {
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(")")
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt b/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
index c602ff3..07e7c09 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
@@ -1,3 +1,5 @@
+@file:Suppress("unused")
+
package io.dico.parcels2
import io.dico.parcels2.util.getPlayerNameOrDefault
@@ -10,10 +12,8 @@ import java.util.*
@Suppress("UsePropertyAccessSyntax")
class ParcelOwner private constructor(val uuid: UUID?,
- name: String?) {
- var name: String? = name
- get() = field ?: getPlayerNameOrDefault(uuid!!).also { field = it }
- private set
+ val name: String?) {
+ val notNullName: String by lazy { name ?: getPlayerNameOrDefault(uuid!!) }
constructor(name: String) : this(null, name)
constructor(uuid: UUID) : this(uuid, null)
@@ -24,13 +24,13 @@ class ParcelOwner private constructor(val uuid: UUID?,
}
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")
+ val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayerExact(name) }
+ val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } }
@Suppress("DEPRECATION")
- val offlinePlayer
- get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
- ?.takeIf { it.isValid }
+ val offlinePlayerAllowingNameMatch: OfflinePlayer? get() = offlinePlayer ?: Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
return uuid?.let { it == player.uniqueId } ?: false
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
index 7bb827c..c94d273 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -64,7 +64,8 @@ class Worlds(val plugin: ParcelsPlugin) {
worldName,
worldOptions,
worldOptions.generator.getGenerator(this, worldName),
- plugin.storage)
+ plugin.storage,
+ plugin.globalAddedData)
} catch (ex: Exception) {
ex.printStackTrace()
@@ -117,7 +118,8 @@ interface ParcelProvider {
class ParcelWorld constructor(val name: String,
val options: WorldOptions,
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 {
Bukkit.getWorld(name) ?: throw NullPointerException("World $name does not appear to be loaded")
}
diff --git a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
index 04e9a26..ccbc7e9 100644
--- a/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
@@ -24,6 +24,8 @@ abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider {
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 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)
}
- 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 parcelSize = o.parcelSize
val absX = x - o.offsetX - pathOffset
val absZ = z - o.offsetZ - pathOffset
val modX = absX umod sectionSize
val modZ = absZ umod sectionSize
- if (0 <= modX && modX < parcelSize && 0 <= modZ && modZ < parcelSize) {
- return world.parcelByID((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
+ if (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
+ return mapper((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
}
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,
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index fa9a696..929b0c7 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -9,7 +9,6 @@ import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelOwner
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.blockvisitor.RegionTraversal
-import io.dico.parcels2.command.NamedParcelDefaultValue.FIRST_OWNED
import io.dico.parcels2.storage.getParcelBySerializedValue
import io.dico.parcels2.util.hasAdminManage
import io.dico.parcels2.util.hasParcelHomeOthers
@@ -49,18 +48,17 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
"more than one parcel",
shortVersion = "teleports you to parcels")
@RequireParameters(0)
- suspend fun cmdHome(player: Player,
- @NamedParcelDefault(FIRST_OWNED) target: NamedParcelTarget): Any? {
- if (player !== target.player && !player.hasParcelHomeOthers) {
+ suspend fun cmdHome(player: Player, @ParcelTarget.Kind(ParcelTarget.OWNER_REAL) target: ParcelTarget): Any? {
+ val ownerTarget = target as ParcelTarget.ByOwner
+ if (!ownerTarget.owner.matches(player) && !player.hasParcelHomeOthers) {
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
.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)
?: error("The specified parcel could not be matched")
@@ -79,7 +77,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
checkParcelLimit(player)
- parcel.owner = ParcelOwner(uuid = player.uuid, name = player.name)
+ parcel.owner = ParcelOwner(player)
return "Enjoy your new parcel!"
}
@@ -100,7 +98,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("swap")
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
-
+ TODO()
}
@Cmd("make_mess")
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt
deleted file mode 100644
index e9472bc..0000000
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt
+++ /dev/null
@@ -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
-
-}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
index dcc3f8c..b5a1abf 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
@@ -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)
- }
-
-}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
new file mode 100644
index 0000000..4dd2825
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -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)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
index affa14e..729bbff 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -107,7 +107,7 @@ class ExposedBacking(private val dataSourceFactory: suspend () -> DataSource) :
setParcelOwner(parcelFor, data.owner)
- for ((uuid, status) in data.added) {
+ for ((uuid, status) in data.addedMap) {
setLocalPlayerStatus(parcelFor, uuid, status)
}
@@ -168,7 +168,7 @@ class ExposedBacking(private val dataSourceFactory: suspend () -> DataSource) :
since = row[ParcelsT.claim_time]
val parcelId = row[ParcelsT.id]
- added = AddedLocalT.readAddedData(parcelId)
+ addedMap = AddedLocalT.readAddedData(parcelId)
AddedLocalT.select { AddedLocalT.attach_id eq parcelId }.forEach {
val uuid = it[AddedLocalT.player_uuid].toUUID()
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
index c75ea9f..e19fd3f 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -32,7 +32,7 @@ sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj,
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 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") {
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)
private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
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 ->
- getId(binaryUuid)
- ?: insertAndGetId("owner(uuid = $uuid)") { it[OwnersT.uuid] = binaryUuid }
+ private inline fun getOrInitId(uuid: UUID, name: String) = uuid.toByteArray().let { binaryUuid ->
+ getId(binaryUuid) ?: insertAndGetId("owner(uuid = $uuid)") {
+ it[this@OwnersT.uuid] = binaryUuid
+ it[this@OwnersT.name] = name
+ }
}
private inline fun getOrInitId(name: String) =
- getId(name)
- ?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
+ getId(name) ?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
override fun getId(owner: ParcelOwner): Int? =
if (owner.hasUUID) getId(owner.uuid!!)
else getId(owner.name!!)
override fun getOrInitId(owner: ParcelOwner): Int =
- if (owner.hasUUID) getOrInitId(owner.uuid!!)
+ if (owner.hasUUID) getOrInitId(owner.uuid!!, owner.notNullName)
else getOrInitId(owner.name!!)
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])
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
index 5c6ce25..ca2943d 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
@@ -19,8 +19,8 @@ object AddedGlobalT : AddedTable<ParcelOwner, ParcelOwner>("parcels_added_global
object ParcelOptionsT : Table("parcel_options") {
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
- val interact_inventory = bool("interact_inventory").default(false)
- val interact_inputs = bool("interact_inputs").default(false)
+ val interact_inventory = bool("interact_inventory").default(true)
+ val interact_inputs = bool("interact_inputs").default(true)
}
typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableMap<UUID, AddedStatus>>>
diff --git a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
index 694f2aa..6db98af 100644
--- a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt
@@ -19,7 +19,7 @@ data class Vec3i(
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 {
const val mask = 0x001F_FFFF
@@ -34,7 +34,8 @@ inline class IVec3i(private val data: Long) {
@Suppress("NOTHING_TO_INLINE")
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
}
}