diff options
author | Dico Karssiens <dico.karssiens@gmail.com> | 2018-08-02 01:16:38 +0100 |
---|---|---|
committer | Dico Karssiens <dico.karssiens@gmail.com> | 2018-08-02 01:16:38 +0100 |
commit | 3917855a72c60d1c78632949b4fea21471873347 (patch) | |
tree | 4dcdbd770b1a74cc307adc0c5a020617d4233351 | |
parent | 472e700e0422d1829aa26e04b74e2077807e75f0 (diff) |
Improve (ParcelTarget)ing for commands, ParcelOwner things, various little bits
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 } } |