summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico <dico.karssiens@gmail.com>2018-08-06 02:53:59 +0100
committerDico <dico.karssiens@gmail.com>2018-08-06 02:53:59 +0100
commit0b8deb4c54f6ad96e0a63692c09a5929f39920fa (patch)
tree0eb5efafc5bb0ff5e16cb931e5348bdf72140cf9
parentba347a805333b84f860150cbf0b8c514472c6f9f (diff)
Add todo.md
-rw-r--r--src/main/kotlin/io/dico/parcels2/AddedData.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelId.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/PlayerProfile.kt1
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt34
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt51
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt3
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt10
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt4
-rw-r--r--todo.md80
11 files changed, 149 insertions, 46 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/AddedData.kt b/src/main/kotlin/io/dico/parcels2/AddedData.kt
index 9249c7f..9835950 100644
--- a/src/main/kotlin/io/dico/parcels2/AddedData.kt
+++ b/src/main/kotlin/io/dico/parcels2/AddedData.kt
@@ -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)
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index e7c5209..a9e5de9 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -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 {
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
index 951a172..20d4cc8 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
@@ -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) }
diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
index c5403cd..2e543dc 100644
--- a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
+++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
@@ -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
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index 7442019..439d653 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -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)
+ }
- val ownedParcelsResult = plugin.storage.getOwnedParcels(ownerTarget.owner).await()
+ @Cmd("tp", aliases = ["teleport"])
+ suspend fun cmdTp(player: Player, @ParcelTarget.Kind(ParcelTarget.ID) target: ParcelTarget): Any? {
+ return cmdGoto(player, target)
+ }
- val ownedParcels = ownedParcelsResult
- .map { worlds.getParcelById(it) }
- .filter { it != null && ownerTarget.world == it.world }
+ @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 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")
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index cbab80a..bbdcdb8 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -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)
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
index 128705f..c154955 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
@@ -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,
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 8ea6653..11d26c4 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -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
}
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 33314aa..45386e8 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -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()
}
diff --git a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
index 877d1cc..24f9401 100644
--- a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
@@ -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)
diff --git a/todo.md b/todo.md
new file mode 100644
index 0000000..959c628
--- /dev/null
+++ b/todo.md
@@ -0,0 +1,80 @@
+# Parcels Todo list
+
+Commands
+-
+Basically all admin commands.
+* setowner
+* dispose
+* reset
+* swap
+* New admin commands that I can't think of right now.
+
+Also
+* setbiome
+* random
+
+Modify home command:
+* Make `:` not be required if prior component cannot be parsed to an int
+* Listen for command events that use plotme-style argument, and transform the command
+
+Add permissions to commands (replace or fix `IContextFilter` from command lib
+to allow inheriting permissions properly).
+
+Parcel Options
+-
+
+Parcel options apply to any player with `DEFAULT` added status.
+They affect what their permissions might be within the parcel.
+
+Apart from `/p option inputs`, `/p option inventory`, the following might be considered.
+
+Move existing options to "interact" namespace (`/p o interact`)
+
+Then,
+* Split `/p option interact inputs` into a list of interactible block types.
+The list could include container blocks, merging the existing inventory option.
+* Players cannot launch projectiles in locations where they can't build.
+This could become optional.
+* Option to control spreading and/or forming of blocks such as grass and ice within the parcel.
+
+Block Management
+-
+Update the parcel corner with owner info when a player flies into the parcel (after migrations).
+Parcels has a player head in that corner in addition to the sign that PlotMe uses.
+
+Commands that modify parcel blocks must be kept track of to prevent multiple
+from running simultaneously in the same parcel. `hasBlockVisitors` field must be updated.
+In general, spamming the commands must be caught at all cost to avoid lots of lag.
+
+Swap - schematic is in place, but proper placement order must be enforced to make sure that attachable
+blocks are placed properly. Alternatively, if a block change method can be found that doesn't
+cause block updates, that would be preferred subject to having good performance.
+
+Change `RegionTraversal` to allow traversing different parts of a region in a different order.
+This could apply to clearing of plots, for example. It would be better if the bottom 64 (floor height)
+layers are done upwards, and the rest downwards.
+
+Events
+-
+Prevent block spreading subject to conditions.
+
+Scan through blocks that were added since original Parcels implementation,
+that might introduce things that need to be checked or listened for.
+
+WorldEdit Listener.
+
+Limit number of beacons in a parcel and/or avoid potion effects being applied outside the parcel.
+
+Database
+-
+Find and patch ways to add new useless entries (for regular players at least)
+
+Prevent invalid player names from being saved to the database.
+Here, invalid player names mean names that contain invalid characters.
+
+Use an atomic GET OR INSERT query so that parallel execution doesn't cause problems
+(as is currently the case when migrating).
+
+Implement a container that doesn't require loading all parcel data on startup (Complex).
+
+