Add todo.md
This commit is contained in:
@@ -17,7 +17,7 @@ interface AddedData {
|
||||
fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean
|
||||
|
||||
fun compareAndSetAddedStatus(key: StatusKey, expect: AddedStatus, status: AddedStatus): Boolean =
|
||||
(getAddedStatus(key) == expect).also { if (it) setAddedStatus(key, status) }
|
||||
getAddedStatus(key) == expect && setAddedStatus(key, status)
|
||||
|
||||
fun isAllowed(key: StatusKey) = getAddedStatus(key) == AddedStatus.ALLOWED
|
||||
fun allow(key: StatusKey) = setAddedStatus(key, AddedStatus.ALLOWED)
|
||||
@@ -36,7 +36,7 @@ interface AddedData {
|
||||
|
||||
inline val OfflinePlayer.statusKey get() = PlayerProfile.nameless(this)
|
||||
|
||||
open class AddedDataHolder(override var addedMap: MutableAddedDataMap = mutableMapOf()) : AddedData {
|
||||
open class AddedDataHolder(override var addedMap: MutableAddedDataMap = MutableAddedDataMap()) : AddedData {
|
||||
override var addedStatusOfStar: AddedStatus = AddedStatus.DEFAULT
|
||||
|
||||
override fun getAddedStatus(key: StatusKey): AddedStatus = addedMap.getOrDefault(key, addedStatusOfStar)
|
||||
|
||||
@@ -44,6 +44,10 @@ interface ParcelData : AddedData {
|
||||
fun isOwner(uuid: UUID): Boolean {
|
||||
return owner?.uuid == uuid
|
||||
}
|
||||
|
||||
fun isOwner(profile: PlayerProfile?): Boolean {
|
||||
return owner == profile
|
||||
}
|
||||
}
|
||||
|
||||
class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf()) : AddedDataHolder(addedMap), ParcelData {
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.UUID
|
||||
interface ParcelWorldId {
|
||||
val name: String
|
||||
val uid: UUID?
|
||||
fun equals(id: ParcelWorldId): Boolean = name == name || (uid != null && uid == id.uid)
|
||||
fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
|
||||
|
||||
val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ interface PlayerProfile {
|
||||
val name: String?
|
||||
val notNullName: String
|
||||
val isStar: Boolean get() = false
|
||||
val exists: Boolean get() = this is RealImpl
|
||||
|
||||
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import io.dico.dicore.command.annotation.Cmd
|
||||
import io.dico.dicore.command.annotation.Desc
|
||||
import io.dico.dicore.command.annotation.Flag
|
||||
import io.dico.dicore.command.annotation.RequireParameters
|
||||
import io.dico.parcels2.PlayerProfile
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.PlayerProfile
|
||||
import io.dico.parcels2.util.hasAdminManage
|
||||
import io.dico.parcels2.util.hasParcelHomeOthers
|
||||
import io.dico.parcels2.util.uuid
|
||||
@@ -44,24 +44,34 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
||||
shortVersion = "teleports you to parcels")
|
||||
@RequireParameters(0)
|
||||
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")
|
||||
return cmdGoto(player, target)
|
||||
}
|
||||
|
||||
@Cmd("tp", aliases = ["teleport"])
|
||||
suspend fun cmdTp(player: Player, @ParcelTarget.Kind(ParcelTarget.ID) target: ParcelTarget): Any? {
|
||||
return cmdGoto(player, target)
|
||||
}
|
||||
|
||||
@Cmd("goto")
|
||||
suspend fun cmdGoto(player: Player, @ParcelTarget.Kind(ParcelTarget.ANY) target: ParcelTarget): Any? {
|
||||
if (target is ParcelTarget.ByOwner) {
|
||||
target.resolveOwner(plugin.storage)
|
||||
if (!target.owner.matches(player) && !player.hasParcelHomeOthers) {
|
||||
error("You do not have permission to teleport to other people's parcels")
|
||||
}
|
||||
}
|
||||
|
||||
val ownedParcelsResult = plugin.storage.getOwnedParcels(ownerTarget.owner).await()
|
||||
|
||||
val ownedParcels = ownedParcelsResult
|
||||
.map { worlds.getParcelById(it) }
|
||||
.filter { it != null && ownerTarget.world == it.world }
|
||||
|
||||
val targetMatch = ownedParcels.getOrNull(target.index)
|
||||
val match = target.getParcelSuspend(plugin.storage)
|
||||
?: error("The specified parcel could not be matched")
|
||||
|
||||
player.teleport(targetMatch.world.getHomeLocation(targetMatch.id))
|
||||
player.teleport(match.world.getHomeLocation(match.id))
|
||||
return ""
|
||||
}
|
||||
|
||||
@Cmd("goto_fake")
|
||||
suspend fun cmdGotoFake(player: Player, @ParcelTarget.Kind(ParcelTarget.OWNER_FAKE) target: ParcelTarget): Any? {
|
||||
return cmdGoto(player, target)
|
||||
}
|
||||
|
||||
@Cmd("claim")
|
||||
@Desc("If this parcel is unowned, makes you the owner",
|
||||
shortVersion = "claims this parcel")
|
||||
|
||||
@@ -5,20 +5,21 @@ 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.Storage
|
||||
import io.dico.parcels2.util.Vec2i
|
||||
import io.dico.parcels2.util.floor
|
||||
import kotlinx.coroutines.experimental.Deferred
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDefault: Boolean) {
|
||||
|
||||
abstract suspend fun ParcelsPlugin.getParcelSuspend(): Parcel?
|
||||
abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
|
||||
|
||||
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend() }
|
||||
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend(storage) }
|
||||
|
||||
class ByID(world: ParcelWorld, val id: Vec2i?, isDefault: Boolean) : ParcelTarget(world, isDefault) {
|
||||
override suspend fun ParcelsPlugin.getParcelSuspend(): Parcel? = getParcel()
|
||||
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
|
||||
}
|
||||
@@ -26,31 +27,31 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
class ByOwner(world: ParcelWorld,
|
||||
owner: PlayerProfile,
|
||||
val index: Int,
|
||||
parsedKind: Int,
|
||||
isDefault: Boolean,
|
||||
val onResolveFailure: (() -> Unit)? = null) : ParcelTarget(world, isDefault) {
|
||||
val onResolveFailure: (() -> Unit)? = null) : ParcelTarget(world, parsedKind, isDefault) {
|
||||
init {
|
||||
if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
|
||||
}
|
||||
|
||||
var owner = owner; private set
|
||||
|
||||
override suspend fun ParcelsPlugin.getParcelSuspend(): Parcel? {
|
||||
onResolveFailure?.let { onFail ->
|
||||
val owner = owner
|
||||
if (owner is PlayerProfile.Unresolved) {
|
||||
val new = owner.tryResolveSuspendedly(storage)
|
||||
if (new == null) {
|
||||
onFail()
|
||||
return@let
|
||||
}
|
||||
this@ByOwner.owner = new
|
||||
}
|
||||
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
|
||||
.map { parcelProvider.getParcelById(it) }
|
||||
.filter { it != null && world == it.world && owner == it.owner }
|
||||
.filter { it.worldId.equals(world.id) }
|
||||
.map { world.getParcelById(it.x, it.z) }
|
||||
|
||||
return ownedParcels.getOrNull(index)
|
||||
}
|
||||
@@ -65,7 +66,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
|
||||
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_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
|
||||
@@ -73,7 +74,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -95,12 +96,12 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
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), false)
|
||||
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, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
|
||||
return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
|
||||
}
|
||||
|
||||
private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
|
||||
@@ -156,10 +157,10 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
|
||||
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, true)
|
||||
return ByID(world, id, kind, true)
|
||||
}
|
||||
|
||||
return ByOwner(world, PlayerProfile(player), 0, true)
|
||||
return ByOwner(world, PlayerProfile(player), 0, kind, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,8 @@ import io.dico.dicore.Formatting
|
||||
import io.dico.parcels2.*
|
||||
import io.dico.parcels2.util.Vec2i
|
||||
import io.dico.parcels2.util.alsoIfTrue
|
||||
import io.dico.parcels2.util.getPlayerName
|
||||
import org.bukkit.OfflinePlayer
|
||||
import org.joda.time.DateTime
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class ParcelImpl(override val world: ParcelWorld,
|
||||
|
||||
@@ -92,8 +92,9 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("RedundantObjectTypeCheck")
|
||||
private fun PlayerProfile.toOwnerProfile(): PlayerProfile {
|
||||
if (this is PlayerProfile.Star) return PlayerProfile.Fake(PlayerProfile.Star.name)
|
||||
if (this is PlayerProfile.Star) return PlayerProfile.Fake(name)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
@@ -91,15 +91,19 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id"
|
||||
|
||||
object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcel_profiles", "owner_id") {
|
||||
val uuid = binary("uuid", 16).nullable()
|
||||
val name = varchar("name", 32)
|
||||
val name = varchar("name", 32).nullable()
|
||||
|
||||
// MySQL dialect MUST permit multiple null values for this to work
|
||||
val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
|
||||
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(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
|
||||
private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
|
||||
|
||||
private inline fun getOrInitId(uuid: UUID, name: String) = uuid.toByteArray().let { binaryUuid -> getOrInitId(
|
||||
private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid -> getOrInitId(
|
||||
{ getId(binaryUuid) },
|
||||
{ it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
|
||||
{ "profile(uuid = $uuid, name = $name)" })
|
||||
@@ -119,7 +123,7 @@ object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcel_profile
|
||||
}
|
||||
|
||||
override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
|
||||
is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.notNullName)
|
||||
is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.name)
|
||||
is PlayerProfile.Fake -> getOrInitId(profile.name)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
|
||||
|
||||
inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
|
||||
inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
|
||||
inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
|
||||
if (this == null) block()
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
|
||||
return EditLoopScope(this).doEditLoop(block)
|
||||
|
||||
Reference in New Issue
Block a user