Archived
0
This commit is contained in:
Dico
2018-09-27 04:41:58 +01:00
parent cdaba0ebd5
commit 307b7aee4a
19 changed files with 166 additions and 256 deletions

View File

@@ -3,13 +3,11 @@
package io.dico.parcels2 package io.dico.parcels2
import io.dico.parcels2.storage.Storage import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.PLAYER_NAME_PLACEHOLDER import io.dico.parcels2.util.ext.PLAYER_NAME_PLACEHOLDER
import io.dico.parcels2.util.getPlayerName
import io.dico.parcels2.util.ext.isValid import io.dico.parcels2.util.ext.isValid
import io.dico.parcels2.util.ext.uuid import io.dico.parcels2.util.ext.uuid
import kotlinx.coroutines.Deferred import io.dico.parcels2.util.getOfflinePlayer
import kotlinx.coroutines.Unconfined import io.dico.parcels2.util.getPlayerName
import kotlinx.coroutines.async
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import java.util.UUID import java.util.UUID
@@ -70,8 +68,7 @@ interface PlayerProfile {
if (input == Star.name) return Star if (input == Star.name) return Star
return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) } return getOfflinePlayer(input)?.let { PlayerProfile(it) } ?: Unresolved(input)
?: Unresolved(input)
} }
} }
@@ -83,8 +80,7 @@ interface PlayerProfile {
override val notNullName: String override val notNullName: String
get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER
val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid } val player: OfflinePlayer? get() = getOfflinePlayer(uuid)
val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid)
override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true) return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true)
@@ -147,10 +143,6 @@ interface PlayerProfile {
return other is Unresolved && name == other.name return other is Unresolved && name == other.name
} }
fun tryResolve(storage: Storage): Deferred<Real?> {
return async(Unconfined) { tryResolveSuspendedly(storage) }
}
suspend fun tryResolveSuspendedly(storage: Storage): Real? { suspend fun tryResolveSuspendedly(storage: Storage): Real? {
return storage.getPlayerUuidForName(name).await()?.let { resolve(it) } return storage.getPlayerUuidForName(name).await()?.let { resolve(it) }
} }
@@ -178,120 +170,9 @@ interface PlayerProfile {
} }
suspend fun PlayerProfile.resolved(storage: Storage, resolveToFake: Boolean = false): PlayerProfile? =
/* when (this) {
is PlayerProfile.Unresolved -> tryResolveSuspendedly(storage)
?: if (resolveToFake) PlayerProfile.Fake(name) else null
/** else -> this
* This class can represent:
*
* An existing player
* A fake player (with only a name)
* An existing player who must have its uuid resolved from the database (after checking against Bukkit OfflinePlayer)
* STAR profile, which matches everyone. This profile is considered a REAL player, because it can have a privilege.
*/
class PlayerProfile2 private constructor(uuid: UUID?,
val name: String?,
val isReal: Boolean = uuid != null) {
private var _uuid: UUID? = uuid
val notNullName: String get() = name ?: getPlayerNameOrDefault(uuid!!)
val uuid: UUID? get() = _uuid ?: if (isReal) throw IllegalStateException("This PlayerProfile must be resolved first") else null
companion object {
// below uuid is just a randomly generated one (version 4). Hopefully no minecraft player will ever have it :)
val star = PlayerProfile(UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1"), "*", true)
fun nameless(player: OfflinePlayer): PlayerProfile {
if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid")
return PlayerProfile(player.uuid)
}
fun fromNameAndUuid(name: String?, uuid: UUID?): PlayerProfile? {
if (name == null && uuid == null) return null
if (star.name == name && star._uuid == uuid) return star
return PlayerProfile(uuid, name)
}
fun realPlayerByName(name: String): PlayerProfile {
return fromString(name, allowReal = true, allowFake = false)
}
fun fromString(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
if (!allowReal) {
if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
return PlayerProfile(input)
}
if (input == star.name) return star
return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) }
?: PlayerProfile(null, input, !allowFake)
}
operator fun createWith(name: String): PlayerProfile {
if (name == star.name) return star
return PlayerProfile(null, name)
}
operator fun createWith(uuid: UUID): PlayerProfile {
if (uuid == star.uuid) return star
return PlayerProfile(uuid, null)
}
operator fun createWith(player: OfflinePlayer): PlayerProfile {
// avoid UUID comparison against STAR
return if (player.isValid) PlayerProfile(player.uuid, player.name) else createWith(player.name)
}
} }
val isStar: Boolean get() = this === star || (name == star.name && _uuid == star._uuid)
val hasUUID: Boolean get() = _uuid != null
val mustBeResolved: Boolean get() = isReal && _uuid == null
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayerExact(name) }
val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } }
val offlinePlayerAllowingNameMatch: OfflinePlayer?
get() = offlinePlayer ?: Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
if (isStar) return true
return uuid?.let { it == player.uniqueId } ?: false
|| (allowNameMatch && name?.let { it == player.name } ?: false)
}
fun equals(other: PlayerProfile): Boolean {
return if (_uuid != null) _uuid == other._uuid
else other._uuid == null && isReal == other.isReal && name == other.name
}
override fun equals(other: Any?): Boolean {
return other is PlayerProfile && equals(other)
}
override fun hashCode(): Int {
return _uuid?.hashCode() ?: name!!.hashCode()
}
/**
* resolve the uuid of this player profile if [mustBeResolved], using specified [storage].
* returns true if the PlayerProfile has a uuid after this call.
*/
suspend fun resolve(storage: Storage): Boolean {
if (mustBeResolved) {
val uuid = storage.getPlayerUuidForName(name!!).await()
_uuid = uuid
return uuid != null
}
return _uuid != null
}
fun resolve(uuid: UUID) {
if (isReal && _uuid == null) {
_uuid = uuid
}
}
}
*/

View File

@@ -24,7 +24,6 @@ data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int)
interface WorkDispatcher { interface WorkDispatcher {
/** /**
* Submit a [task] that should be run synchronously, but limited such that it does not stall the server * Submit a [task] that should be run synchronously, but limited such that it does not stall the server
* a bunch
*/ */
fun dispatch(task: WorkerTask): Worker fun dispatch(task: WorkerTask): Worker
@@ -89,11 +88,6 @@ interface Worker : WorkerAndScopeMembersUnion {
* Await completion of this worker * Await completion of this worker
*/ */
suspend fun awaitCompletion() suspend fun awaitCompletion()
/**
* An object attached to this worker
*/
//val attachment: Any?
} }
interface WorkerScope : WorkerAndScopeMembersUnion { interface WorkerScope : WorkerAndScopeMembersUnion {
@@ -114,7 +108,7 @@ interface WorkerScope : WorkerAndScopeMembersUnion {
/** /**
* Get a [WorkerScope] that is responsible for [portion] part of the progress * Get a [WorkerScope] that is responsible for [portion] part of the progress
* If [portion] is negative, the remainder of the progress is used * If [portion] is negative, the remaining progress is used
*/ */
fun delegateWork(portion: Double = -1.0): WorkerScope fun delegateWork(portion: Double = -1.0): WorkerScope
} }

View File

@@ -20,12 +20,8 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName) return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
} }
protected fun error(message: String): Nothing {
throw CommandException(message)
}
protected fun checkConnected(action: String) { protected fun checkConnected(action: String) {
if (!plugin.storage.isConnected) error("Parcels cannot $action right now because of a database error") if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
} }
protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) { protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
@@ -35,7 +31,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
val limit = player.parcelLimit val limit = player.parcelLimit
if (numOwnedParcels >= limit) { if (numOwnedParcels >= limit) {
error("You have enough plots for now") err("You have enough plots for now")
} }
} }
@@ -65,10 +61,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
} }
} }
protected fun err(message: String): Nothing = throw CommandException(message)
override fun getCoroutineContext() = plugin.coroutineContext override fun getCoroutineContext() = plugin.coroutineContext
} }
fun err(message: String): Nothing = throw CommandException(message)

View File

@@ -8,18 +8,19 @@ import io.dico.dicore.command.annotation.Flag
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.Privilege import io.dico.parcels2.Privilege
import io.dico.parcels2.command.ParcelTarget.Companion.ID import io.dico.parcels2.command.ParcelTarget.TargetKind
import io.dico.parcels2.command.ParcelTarget.Kind import io.dico.parcels2.resolved
class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("setowner") @Cmd("setowner")
@RequireParcelPrivilege(Privilege.ADMIN) @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdSetowner(target: PlayerProfile): Any? { suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
parcel.owner = target val profile = target.resolved(plugin.storage, resolveToFake = true)!!
parcel.owner = profile
val fakeString = if (target.isFake) " (fake)" else "" val fakeString = if (profile.isFake) " (fake)" else ""
return "${target.notNullName}$fakeString is the new owner of (${parcel.id.idString})" return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
} }
@Cmd("dispose") @Cmd("dispose")
@@ -43,7 +44,7 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("swap") @Cmd("swap")
@RequireParcelPrivilege(Privilege.ADMIN) @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdSwap(context: ExecutionContext, fun ParcelScope.cmdSwap(context: ExecutionContext,
@Kind(ID) target: ParcelTarget, @TargetKind(TargetKind.ID) target: ParcelTarget,
@Flag sure: Boolean): Any? { @Flag sure: Boolean): Any? {
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel") Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
if (!sure) return areYouSureMessage(context) if (!sure) return areYouSureMessage(context)

View File

@@ -9,16 +9,14 @@ import io.dico.dicore.command.annotation.Desc
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.PrivilegeChangeResult.* import io.dico.parcels2.PrivilegeChangeResult.*
import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
import io.dico.parcels2.util.ext.hasPermAdminManage
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
private val data private val data
inline get() = plugin.globalPrivileges inline get() = plugin.globalPrivileges
private fun checkContext(context: ExecutionContext, owner: OfflinePlayer): OfflinePlayer { private fun checkContext(context: ExecutionContext, owner: OfflinePlayer): OfflinePlayer {
checkConnected("have privileges changed")
val sender = context.sender val sender = context.sender
if (sender !== owner) { if (sender !== owner) {
Validate.isAuthorized(sender, PERM_ADMIN_MANAGE) Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)

View File

@@ -1,14 +1,17 @@
package io.dico.parcels2.command package io.dico.parcels2.command
import io.dico.dicore.command.* import io.dico.dicore.command.*
import io.dico.dicore.command.IContextFilter.Priority.* import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
import io.dico.dicore.command.annotation.Cmd import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.PreprocessArgs import io.dico.dicore.command.annotation.PreprocessArgs
import io.dico.dicore.command.annotation.RequireParameters
import io.dico.dicore.command.parameter.ArgumentBuffer import io.dico.dicore.command.parameter.ArgumentBuffer
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.*
import io.dico.parcels2.Privilege
import io.dico.parcels2.blockvisitor.RegionTraverser import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.doBlockOperation import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
import kotlinx.coroutines.launch
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.block.BlockFace import org.bukkit.block.BlockFace
@@ -29,7 +32,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
if (worldName == "list") { if (worldName == "list") {
return Bukkit.getWorlds().joinToString("\n- ", "- ", "") return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
} }
val world = Bukkit.getWorld(worldName) ?: throw CommandException("World $worldName is not loaded") val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
sender.teleport(world.spawnLocation) sender.teleport(world.spawnLocation)
return "Teleported you to $worldName spawn" return "Teleported you to $worldName spawn"
} }
@@ -76,44 +79,64 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
blockData.javaClass.interfaces!!.contentToString() blockData.javaClass.interfaces!!.contentToString()
} }
@Cmd("visitors") @Cmd("jobs")
fun cmdVisitors(): Any? { fun cmdJobs(): Any? {
val workers = plugin.workDispatcher.workers val workers = plugin.workDispatcher.workers
println(workers.map { it.job }.joinToString(separator = "\n")) println(workers.map { it.job }.joinToString(separator = "\n"))
return "Task count: ${workers.size}" return "Task count: ${workers.size}"
} }
@Cmd("force_visitors") @Cmd("complete_jobs")
fun cmdForceVisitors(): Any? { fun cmdCompleteJobs(): Any? = cmdJobs().also {
val workers = plugin.workDispatcher.workers plugin.launch { plugin.workDispatcher.completeAllTasks() }
plugin.workDispatcher.completeAllTasks()
return "Completed task count: ${workers.size}"
}
@Cmd("hasperm")
fun cmdHasperm(sender: CommandSender, target: Player, permission: String): Any? {
return target.hasPermission(permission).toString()
} }
@Cmd("message") @Cmd("message")
@PreprocessArgs @PreprocessArgs
fun cmdMessage(sender: CommandSender, message: String): Any? { fun cmdMessage(sender: CommandSender, message: String): Any? {
// testing @PreprocessArgs which merges "hello there" into a single argument
sender.sendMessage(message) sender.sendMessage(message)
return null return null
} }
@Cmd("permissions") @Cmd("hasperm")
fun cmdPermissions(context: ExecutionContext, vararg address: String): Any? { fun cmdHasperm(target: Player, permission: String): Any? {
val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address)) return target.hasPermission(permission).toString()
Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
val permissions = getPermissionsOf(target)
return permissions.joinToString(separator = "\n")
} }
private fun getPermissionsOf(address: ICommandAddress, @Cmd("permissions")
path: Array<String> = emptyArray(), fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
result: MutableList<String> = mutableListOf()): List<String> { val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
}
@Cmd("privilege")
@RequireParameters(1)
suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
val key = toPrivilegeKey(target)
val perm = when (adminPerm) {
"none" -> null
"build" -> PERM_BUILD_ANYWHERE
"manage", null -> PERM_ADMIN_MANAGE
"enter" -> PERM_BAN_BYPASS
else -> err("adminPerm should be build, manager or enter")
}
val privilege = if (perm == null) {
parcel.getStoredPrivilege(key)
} else {
if (key is PlayerProfile.Star) err("* can't have permissions")
parcel.getEffectivePrivilege(key.player!!, perm)
}
return privilege.toString()
}
private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
val command = address.command ?: return result val command = address.command ?: return result
var inherited = false var inherited = false

View File

@@ -9,7 +9,7 @@ import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.Privilege import io.dico.parcels2.Privilege
import io.dico.parcels2.command.ParcelTarget.Kind import io.dico.parcels2.command.ParcelTarget.TargetKind
import io.dico.parcels2.util.ext.hasParcelHomeOthers import io.dico.parcels2.util.ext.hasParcelHomeOthers
import io.dico.parcels2.util.ext.hasPermAdminManage import io.dico.parcels2.util.ext.hasPermAdminManage
import io.dico.parcels2.util.ext.uuid import io.dico.parcels2.util.ext.uuid
@@ -29,7 +29,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
checkParcelLimit(player, world) checkParcelLimit(player, world)
val parcel = world.nextEmptyParcel() val parcel = world.nextEmptyParcel()
?: error("This world is full, please ask an admin to upsize it") ?: err("This world is full, please ask an admin to upsize it")
parcel.owner = PlayerProfile(uuid = player.uuid) parcel.owner = PlayerProfile(uuid = player.uuid)
player.teleport(parcel.homeLocation) player.teleport(parcel.homeLocation)
return "Enjoy your new parcel!" return "Enjoy your new parcel!"
@@ -58,7 +58,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@RequireParameters(0) @RequireParameters(0)
suspend fun cmdHome( suspend fun cmdHome(
player: Player, player: Player,
@Kind(ParcelTarget.OWNER_REAL) target: ParcelTarget @TargetKind(TargetKind.OWNER_REAL) target: ParcelTarget
): Any? { ): Any? {
return cmdGoto(player, target) return cmdGoto(player, target)
} }
@@ -66,7 +66,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@Cmd("tp", aliases = ["teleport"]) @Cmd("tp", aliases = ["teleport"])
suspend fun cmdTp( suspend fun cmdTp(
player: Player, player: Player,
@Kind(ParcelTarget.ID) target: ParcelTarget @TargetKind(TargetKind.ID) target: ParcelTarget
): Any? { ): Any? {
return cmdGoto(player, target) return cmdGoto(player, target)
} }
@@ -74,17 +74,17 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@Cmd("goto") @Cmd("goto")
suspend fun cmdGoto( suspend fun cmdGoto(
player: Player, player: Player,
@Kind(ParcelTarget.ANY) target: ParcelTarget @TargetKind(TargetKind.ANY) target: ParcelTarget
): Any? { ): Any? {
if (target is ParcelTarget.ByOwner) { if (target is ParcelTarget.ByOwner) {
target.resolveOwner(plugin.storage) target.resolveOwner(plugin.storage)
if (!target.owner.matches(player) && !player.hasParcelHomeOthers) { if (!target.owner.matches(player) && !player.hasParcelHomeOthers) {
error("You do not have permission to teleport to other people's parcels") err("You do not have permission to teleport to other people's parcels")
} }
} }
val match = target.getParcelSuspend(plugin.storage) val match = target.getParcelSuspend(plugin.storage)
?: error("The specified parcel could not be matched") ?: err("The specified parcel could not be matched")
player.teleport(match.homeLocation) player.teleport(match.homeLocation)
return null return null
} }
@@ -92,7 +92,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@Cmd("goto_fake") @Cmd("goto_fake")
suspend fun cmdGotoFake( suspend fun cmdGotoFake(
player: Player, player: Player,
@Kind(ParcelTarget.OWNER_FAKE) target: ParcelTarget @TargetKind(TargetKind.OWNER_FAKE) target: ParcelTarget
): Any? { ): Any? {
return cmdGoto(player, target) return cmdGoto(player, target)
} }
@@ -105,7 +105,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
suspend fun ParcelScope.cmdClaim(player: Player): Any? { suspend fun ParcelScope.cmdClaim(player: Player): Any? {
checkConnected("be claimed") checkConnected("be claimed")
parcel.owner.takeIf { !player.hasPermAdminManage }?.let { parcel.owner.takeIf { !player.hasPermAdminManage }?.let {
error(if (it.matches(player)) "You already own this parcel" else "This parcel is not available") err(if (it.matches(player)) "You already own this parcel" else "This parcel is not available")
} }
checkParcelLimit(player, world) checkParcelLimit(player, world)
@@ -117,6 +117,7 @@ class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : Ab
@Desc("Unclaims this parcel") @Desc("Unclaims this parcel")
@RequireParcelPrivilege(Privilege.OWNER) @RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdUnclaim(player: Player): Any? { fun ParcelScope.cmdUnclaim(player: Player): Any? {
checkConnected("be unclaimed")
parcel.dispose() parcel.dispose()
return "Your parcel has been disposed" return "Your parcel has been disposed"
} }

View File

@@ -28,6 +28,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.OWNER) @RequireParcelPrivilege(Privilege.OWNER)
suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)
@@ -46,6 +47,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.OWNER) @RequireParcelPrivilege(Privilege.OWNER)
suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)
@@ -63,6 +65,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.CAN_MANAGE) @RequireParcelPrivilege(Privilege.CAN_MANAGE)
suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)
@@ -83,6 +86,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.CAN_MANAGE) @RequireParcelPrivilege(Privilege.CAN_MANAGE)
suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)
@@ -103,6 +107,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.CAN_MANAGE) @RequireParcelPrivilege(Privilege.CAN_MANAGE)
suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)
@@ -123,6 +128,7 @@ class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(pl
) )
@RequireParcelPrivilege(Privilege.CAN_MANAGE) @RequireParcelPrivilege(Privilege.CAN_MANAGE)
suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? { suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
checkConnected("have privileges changed")
checkOwned(sender) checkOwned(sender)
val key = toPrivilegeKey(player) val key = toPrivilegeKey(player)

View File

@@ -33,7 +33,7 @@ fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher = CommandBuilde
) )
group("interact", "i") { group("interact", "i") {
val command = ParcelOptionsInteractCommand(plugin.parcelProvider) val command = ParcelOptionsInteractCommand(plugin)
Interactables.classesById.forEach { Interactables.classesById.forEach {
addSubCommand(it.name, command) addSubCommand(it.name, command)
} }

View File

@@ -4,11 +4,12 @@ import io.dico.dicore.command.*
import io.dico.dicore.command.parameter.type.ParameterTypes import io.dico.dicore.command.parameter.type.ParameterTypes
import io.dico.parcels2.Interactables import io.dico.parcels2.Interactables
import io.dico.parcels2.ParcelProvider import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.Privilege import io.dico.parcels2.Privilege
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
class ParcelOptionsInteractCommand(val parcelProvider: ParcelProvider) : Command() { class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
init { init {
setShortDescription("View and/or change the setting") setShortDescription("View and/or change the setting")
@@ -20,10 +21,12 @@ class ParcelOptionsInteractCommand(val parcelProvider: ParcelProvider) : Command
} }
override fun execute(sender: CommandSender, context: ExecutionContext): String? { override fun execute(sender: CommandSender, context: ExecutionContext): String? {
if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
val interactableClass = Interactables[context.address.mainKey] val interactableClass = Interactables[context.address.mainKey]
val allowed: Boolean? = context.get("allowed") val allowed: Boolean? = context.get("allowed")
val parcel = parcelProvider.getParcelRequired(sender as Player, val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE) if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
if (allowed == null) { if (allowed == null) {
@@ -52,5 +55,3 @@ class ParcelOptionsInteractCommand(val parcelProvider: ParcelProvider) : Command
} }
} }
private fun err(message: String): Nothing = throw CommandException(message)

View File

@@ -3,8 +3,12 @@ package io.dico.parcels2.command
import io.dico.dicore.command.CommandException import io.dico.dicore.command.CommandException
import io.dico.dicore.command.parameter.ArgumentBuffer import io.dico.dicore.command.parameter.ArgumentBuffer
import io.dico.dicore.command.parameter.Parameter 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.dicore.command.parameter.type.ParameterType
import io.dico.parcels2.* import io.dico.parcels2.*
import io.dico.parcels2.command.ProfileKind.Companion.ANY
import io.dico.parcels2.command.ProfileKind.Companion.FAKE
import io.dico.parcels2.command.ProfileKind.Companion.REAL
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -43,11 +47,27 @@ class ParcelParameterType(val parcelProvider: ParcelProvider) : ParameterType<Pa
} }
class ProfileParameterType : ParameterType<PlayerProfile, Void>(PlayerProfile::class.java) { annotation class ProfileKind(val kind: Int) {
companion object : ParameterConfig<ProfileKind, Int>(ProfileKind::class.java) {
const val REAL = 1
const val FAKE = 2
const val ANY = 4
override fun toParameterInfo(annotation: ProfileKind): Int {
return annotation.kind
}
}
}
class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::class.java, ProfileKind) {
override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile {
val info = parameter.paramInfo ?: REAL
val allowReal = info and REAL != 0
val allowFake = info and FAKE != 0
override fun parse(parameter: Parameter<PlayerProfile, Void>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile {
val input = buffer.next() val input = buffer.next()
return PlayerProfile.byName(input, allowReal = true, allowFake = true) return PlayerProfile.byName(input, allowReal, allowFake)
} }
} }

View File

@@ -4,13 +4,20 @@ import io.dico.dicore.command.parameter.ArgumentBuffer
import io.dico.dicore.command.parameter.Parameter import io.dico.dicore.command.parameter.Parameter
import io.dico.dicore.command.parameter.type.ParameterConfig import io.dico.dicore.command.parameter.type.ParameterConfig
import io.dico.dicore.command.parameter.type.ParameterType import io.dico.dicore.command.parameter.type.ParameterType
import io.dico.parcels2.* 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.storage.Storage
import io.dico.parcels2.util.Vec2i import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.ext.floor import io.dico.parcels2.util.ext.floor
import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -18,8 +25,6 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
abstract suspend fun getParcelSuspend(storage: Storage): Parcel? abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = async(start = UNDISPATCHED) { getParcelSuspend(storage) }
class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) { class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel() override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel()
fun getParcel() = id?.let { world.getParcelById(it) } fun getParcel() = id?.let { world.getParcelById(it) }
@@ -61,29 +66,29 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
} }
} }
companion object { annotation class TargetKind(val kind: Int) {
const val ID = 1 // ID companion object : ParameterConfig<TargetKind, Int>(TargetKind::class.java) {
const val OWNER_REAL = 2 // an owner backed by a UUID const val ID = 1 // ID
const val OWNER_FAKE = 4 // an owner not backed by a UUID 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 OWNER = OWNER_REAL or OWNER_FAKE // any owner
const val ANY = ID or OWNER_REAL or OWNER_FAKE // any 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 REAL = ID or OWNER_REAL // no owner not backed by a UUID
const val DEFAULT_KIND = REAL 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 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 // instead of parcel that the player is in
}
annotation class Kind(val kind: Int) override fun toParameterInfo(annotation: TargetKind): Int {
private object Config : ParameterConfig<Kind, Int>(Kind::class.java) { return annotation.kind
override fun toParameterInfo(annotation: Kind): Int { }
return annotation.kind
} }
} }
class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) { 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 { override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
var input = buffer.next() var input = buffer.next()

View File

@@ -219,7 +219,7 @@ class DefaultParcelGenerator(
skullBlock.type = Material.PLAYER_HEAD skullBlock.type = Material.PLAYER_HEAD
val skull = skullBlock.state as Skull val skull = skullBlock.state as Skull
if (owner is PlayerProfile.Real) { if (owner is PlayerProfile.Real) {
skull.owningPlayer = owner.playerUnchecked skull.owningPlayer = Bukkit.getOfflinePlayer(owner.uuid)
} else { } else {
skull.owner = owner.name skull.owner = owner.name
} }

View File

@@ -1,16 +0,0 @@
package io.dico.parcels2.listener
import io.dico.dicore.RegistratorListener
import io.dico.parcels2.ParcelsPlugin
import org.bukkit.event.Event
interface HasPlugin {
val plugin: ParcelsPlugin
}
inline fun <reified T : Event> HasPlugin.listener(crossinline block: suspend (T) -> Unit) = RegistratorListener<T> { event ->
}

View File

@@ -60,7 +60,7 @@ class ParcelListeners(
val user = event.player val user = event.player
if (user.hasPermBanBypass) return@l if (user.hasPermBanBypass) return@l
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
if (!parcel.canEnter(user)) { if (!parcel.canEnterFast(user)) {
parcelProvider.getParcelAt(event.from)?.also { parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.homeLocation) user.teleport(it.homeLocation)
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel") user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")

View File

@@ -0,0 +1,12 @@
package io.dico.parcels2.util
import io.dico.parcels2.util.ext.isValid
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import java.util.UUID
fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name
fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid }

View File

@@ -1,14 +0,0 @@
package io.dico.parcels2.util
import io.dico.parcels2.util.ext.isValid
import org.bukkit.Bukkit
import java.nio.ByteBuffer
import java.util.UUID
const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
fun getPlayerName(uuid: UUID): String? {
return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name
}

View File

@@ -2,7 +2,10 @@ package io.dico.parcels2.util.ext
import io.dico.dicore.Formatting import io.dico.dicore.Formatting
import io.dico.parcels2.logger import io.dico.parcels2.logger
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import java.io.File import java.io.File
import java.util.UUID
fun File.tryCreate(): Boolean { fun File.tryCreate(): Boolean {
if (exists()) { if (exists()) {

View File

@@ -53,3 +53,5 @@ fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false,
sendMessage(prefix + Formatting.translateChars('&', message)) sendMessage(prefix + Formatting.translateChars('&', message))
} }
} }
const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"