Archived
0

Improve (ParcelTarget)ing for commands, ParcelOwner things, various little bits

This commit is contained in:
Dico Karssiens
2018-08-02 01:16:38 +01:00
parent 472e700e04
commit 3917855a72
14 changed files with 307 additions and 165 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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(")")
}
}
}
}

View File

@@ -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

View File

@@ -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")
} }

View File

@@ -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)

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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)
}
}

View 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)
}
}
}

View File

@@ -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()

View File

@@ -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])
} }
} }

View File

@@ -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>>>

View File

@@ -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
} }
} }