diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt')
-rw-r--r-- | src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt | 406 |
1 files changed, 215 insertions, 191 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt index c39c4b6..934a993 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt @@ -1,191 +1,215 @@ -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.Parcel
-import io.dico.parcels2.ParcelProvider
-import io.dico.parcels2.ParcelWorld
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.DEFAULT_KIND
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.ID
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_FAKE
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_REAL
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.floor
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-
-sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDefault: Boolean) {
-
- abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
-
- class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
- override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel()
- fun getParcel() = id?.let { world.getParcelById(it) }
- val isPath: Boolean get() = id == null
- }
-
- class ByOwner(
- world: ParcelWorld,
- owner: PlayerProfile,
- val index: Int,
- parsedKind: Int,
- isDefault: Boolean,
- val onResolveFailure: (() -> Unit)? = null
- ) : ParcelTarget(world, parsedKind, isDefault) {
- init {
- if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
- }
-
- var owner = owner; private set
-
- suspend fun resolveOwner(storage: Storage): Boolean {
- val owner = owner
- if (owner is PlayerProfile.Unresolved) {
- this.owner = owner.tryResolveSuspendedly(storage) ?: if (parsedKind and OWNER_FAKE != 0) PlayerProfile.Fake(owner.name)
- else run { onResolveFailure?.invoke(); return false }
- }
- return true
- }
-
- override suspend fun getParcelSuspend(storage: Storage): Parcel? {
- onResolveFailure?.let { resolveOwner(storage) }
-
- val ownedParcelsSerialized = storage.getOwnedParcels(owner).await()
- val ownedParcels = ownedParcelsSerialized
- .filter { it.worldId.equals(world.id) }
- .map { world.getParcelById(it.x, it.z) }
-
- return ownedParcels.getOrNull(index)
- }
- }
-
- annotation class TargetKind(val kind: Int) {
- companion object : ParameterConfig<TargetKind, Int>(TargetKind::class.java) {
- const val ID = 1 // ID
- const val OWNER_REAL = 2 // an owner backed by a UUID
- const val OWNER_FAKE = 4 // 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 = 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
- // instead of parcel that the player is in
-
- override fun toParameterInfo(annotation: TargetKind): Int {
- return annotation.kind
- }
- }
- }
-
- class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) :
- ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) {
-
- 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")
- parcelProvider.getWorld(player.world)
- ?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world")
- } else {
- parcelProvider.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 OWNER, that is, an owner and index")
- return ByID(world, getId(parameter, input), kind, false)
- }
-
- if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
- val (owner, index) = getHomeIndex(parameter, kind, sender, input)
- return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
- }
-
- 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<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
- val splitIdx = input.indexOf(':')
- val ownerString: String
- val index: Int?
-
- val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex
-
- if (splitIdx == -1) {
-
- if (speciallyParsedIndex == null) {
- // just the index.
- index = input.toIntOrNull()
- ownerString = if (index == null) input else ""
- } else {
- // just the owner.
- index = speciallyParsedIndex
- ownerString = input
- }
-
- } else {
- if (speciallyParsedIndex != null) {
- invalidInput(parameter, "Duplicate home index")
- }
-
- ownerString = input.substring(0, splitIdx)
-
- val indexString = input.substring(splitIdx + 1)
- index = indexString.toIntOrNull()
- ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
- }
-
- val owner = if (ownerString.isEmpty())
- PlayerProfile(requirePlayer(sender, parameter, "the player"))
- else
- PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
-
- return owner to (index ?: 0)
- }
-
- private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player {
- if (sender !is Player) invalidInput(parameter, "console cannot omit the $objName")
- return sender
- }
-
- 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 = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel")
- if (useLocation) {
- val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos }
- return ByID(world, id, kind, true)
- }
-
- return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true)
- }
- }
-
-}
+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.Parcel +import io.dico.parcels2.ParcelProvider +import io.dico.parcels2.ParcelWorld +import io.dico.parcels2.PlayerProfile +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.DEFAULT_KIND +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.ID +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_FAKE +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_REAL +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT +import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL +import io.dico.parcels2.storage.Storage +import io.dico.parcels2.util.math.Vec2i +import io.dico.parcels2.util.math.floor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDefault: Boolean) { + + abstract suspend fun getParcelSuspend(storage: Storage): Parcel? + + class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : + ParcelTarget(world, parsedKind, isDefault) { + override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel() + fun getParcel() = id?.let { world.getParcelById(it) } + val isPath: Boolean get() = id == null + } + + class ByOwner( + world: ParcelWorld, + owner: PlayerProfile, + val index: Int, + parsedKind: Int, + isDefault: Boolean, + val onResolveFailure: (() -> Unit)? = null + ) : ParcelTarget(world, parsedKind, isDefault) { + init { + if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index") + } + + var owner = owner; private set + + suspend fun resolveOwner(storage: Storage): Boolean { + val owner = owner + if (owner is PlayerProfile.Unresolved) { + this.owner = owner.tryResolveSuspendedly(storage) ?: if (parsedKind and OWNER_FAKE != 0) PlayerProfile.Fake(owner.name) + else run { onResolveFailure?.invoke(); return false } + } + return true + } + + override suspend fun getParcelSuspend(storage: Storage): Parcel? { + onResolveFailure?.let { resolveOwner(storage) } + + val ownedParcelsSerialized = storage.getOwnedParcels(owner).await() + val ownedParcels = ownedParcelsSerialized + .filter { it.worldId.equals(world.id) } + .map { world.getParcelById(it.x, it.z) } + + return ownedParcels.getOrNull(index) + } + } + + annotation class TargetKind(val kind: Int) { + companion object : ParameterConfig<TargetKind, Int>(TargetKind::class.java) { + const val ID = 1 // ID + const val OWNER_REAL = 2 // an owner backed by a UUID + const val OWNER_FAKE = 4 // 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 = + 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default + // instead of parcel that the player is in + + override fun toParameterInfo(annotation: TargetKind): Int { + return annotation.kind + } + } + } + + class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) : + ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) { + + 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") + parcelProvider.getWorld(player.world) + ?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world") + } else { + parcelProvider.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 OWNER, that is, an owner and index") + return ByID(world, getId(parameter, input), kind, false) + } + + if (kind and OWNER == 0) invalidInput(parameter, + "You must specify a parcel by ID, that is, the x and z component separated by a comma") + val (owner, index) = getHomeIndex(parameter, kind, sender, input) + return ByOwner(world, + owner, + index, + kind, + false, + onResolveFailure = { invalidInput(parameter, "The player $input does not exist") }) + } + + 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<*, *>, + kind: Int, + sender: CommandSender, + input: String + ): Pair<PlayerProfile, Int> { + val splitIdx = input.indexOf(':') + val ownerString: String + val index: Int? + + val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex + + if (splitIdx == -1) { + + if (speciallyParsedIndex == null) { + // just the index. + index = input.toIntOrNull() + ownerString = if (index == null) input else "" + } else { + // just the owner. + index = speciallyParsedIndex + ownerString = input + } + + } else { + if (speciallyParsedIndex != null) { + invalidInput(parameter, "Duplicate home index") + } + + ownerString = input.substring(0, splitIdx) + + val indexString = input.substring(splitIdx + 1) + index = indexString.toIntOrNull() + ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer") + } + + val owner = (if (ownerString.isEmpty()) + PlayerProfile(requirePlayer(sender, parameter, "the player")) + else + PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)) + ?: invalidInput(parameter, "\'$ownerString\' is not a valid player name") + + return owner to (index ?: 0) + } + + private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player { + if (sender !is Player) invalidInput(parameter, "console cannot omit the $objName") + return sender + } + + 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 = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, + "You must be in a parcel world to omit the parcel") + if (useLocation) { + val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos } + return ByID(world, id, kind, true) + } + + return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true) + } + } + +} |