summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico <dico.karssiens@gmail.com>2018-09-24 02:45:41 +0100
committerDico <dico.karssiens@gmail.com>2018-09-24 02:45:41 +0100
commit1a440767b3d8d7b267ecfeeb37f8c315bae9d112 (patch)
tree7250af6b41be34f41c2b5702922bb1d3948f15ce
parente0bf8249bdf23386039d395ec55b2a011c2e09ac (diff)
Replace AddedData API with Privileges API, adding CAN_MANAGE and required changes
-rw-r--r--build.gradle.kts3
-rw-r--r--src/main/kotlin/io/dico/parcels2/AddedData.kt62
-rw-r--r--src/main/kotlin/io/dico/parcels2/Interactable.kt8
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt13
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/Privileges.kt134
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt56
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt10
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt9
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt10
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsParcelOptions.kt61
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt (renamed from src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt)66
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt90
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt19
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt41
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt36
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt16
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt38
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt38
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt53
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Backing.kt12
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Storage.kt27
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt57
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt42
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt8
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Player.kt10
33 files changed, 552 insertions, 413 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index 34a9737..098f1af 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -87,10 +87,11 @@ dependencies {
tasks {
removeIf { it is ShadowJar }
- val compileKotlin by getting(KotlinCompile::class) {
+ tasks.withType<KotlinCompile> {
kotlinOptions {
javaParameters = true
suppressWarnings = true
+ jvmTarget = "1.8"
//freeCompilerArgs = listOf("-XXLanguage:+InlineClasses", "-Xuse-experimental=kotlin.Experimental")
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/AddedData.kt b/src/main/kotlin/io/dico/parcels2/AddedData.kt
deleted file mode 100644
index a23b36e..0000000
--- a/src/main/kotlin/io/dico/parcels2/AddedData.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.AddedStatus.*
-import org.bukkit.OfflinePlayer
-
-enum class AddedStatus {
- DEFAULT, ALLOWED, BANNED;
-}
-
-typealias StatusKey = PlayerProfile.Real
-typealias MutableAddedDataMap = MutableMap<StatusKey, AddedStatus>
-typealias AddedDataMap = Map<StatusKey, AddedStatus>
-
-@Suppress("FunctionName")
-fun MutableAddedDataMap(): MutableAddedDataMap = hashMapOf()
-
-interface AddedData {
- val addedMap: AddedDataMap
- var statusOfStar: AddedStatus
-
- fun getStatus(key: StatusKey): AddedStatus
- fun setStatus(key: StatusKey, status: AddedStatus): Boolean
-
- fun casStatus(key: StatusKey, expect: AddedStatus, status: AddedStatus): Boolean =
- getStatus(key) == expect && setStatus(key, status)
-
- fun isAllowed(key: StatusKey) = getStatus(key) == ALLOWED
- fun allow(key: StatusKey) = setStatus(key, ALLOWED)
- fun disallow(key: StatusKey) = casStatus(key, ALLOWED, DEFAULT)
- fun isBanned(key: StatusKey) = getStatus(key) == BANNED
- fun ban(key: StatusKey) = setStatus(key, BANNED)
- fun unban(key: StatusKey) = casStatus(key, BANNED, DEFAULT)
-
- fun isAllowed(player: OfflinePlayer) = isAllowed(player.statusKey)
- fun allow(player: OfflinePlayer) = allow(player.statusKey)
- fun disallow(player: OfflinePlayer) = disallow(player.statusKey)
- fun isBanned(player: OfflinePlayer) = isBanned(player.statusKey)
- fun ban(player: OfflinePlayer) = ban(player.statusKey)
- fun unban(player: OfflinePlayer) = unban(player.statusKey)
-}
-
-inline val OfflinePlayer.statusKey: StatusKey
- get() = PlayerProfile.nameless(this)
-
-open class AddedDataHolder(override var addedMap: MutableAddedDataMap = MutableAddedDataMap()) : AddedData {
- override var statusOfStar: AddedStatus = DEFAULT
-
- override fun getStatus(key: StatusKey): AddedStatus = addedMap.getOrDefault(key, statusOfStar)
-
- override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
- return if (status == DEFAULT) addedMap.remove(key) != null
- else addedMap.put(key, status) != status
- }
-}
-
-interface GlobalAddedData : AddedData {
- val owner: PlayerProfile
-}
-
-interface GlobalAddedDataManager {
- operator fun get(owner: PlayerProfile): GlobalAddedData
-}
diff --git a/src/main/kotlin/io/dico/parcels2/Interactable.kt b/src/main/kotlin/io/dico/parcels2/Interactable.kt
index 60539aa..cb83d6e 100644
--- a/src/main/kotlin/io/dico/parcels2/Interactable.kt
+++ b/src/main/kotlin/io/dico/parcels2/Interactable.kt
@@ -2,7 +2,6 @@ package io.dico.parcels2
import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
import org.bukkit.Material
-import java.lang.IllegalArgumentException
import java.util.EnumMap
class Interactables
@@ -113,13 +112,14 @@ val pathInteractableConfig: InteractableConfiguration = run {
interface InteractableConfiguration {
val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
+
fun isInteractable(material: Material): Boolean
fun isInteractable(clazz: Interactables): Boolean
fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
fun clear(): Boolean
- fun copyFrom(other: InteractableConfiguration) {
- Interactables.classesById.forEach { setInteractable(it, other.isInteractable(it)) }
- }
+
+ fun copyFrom(other: InteractableConfiguration) =
+ Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
operator fun invoke(material: Material) = isInteractable(material)
operator fun invoke(className: String) = isInteractable(Interactables[className])
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index 2c56627..6ec6121 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -1,7 +1,7 @@
package io.dico.parcels2
import io.dico.parcels2.util.Vec2i
-import io.dico.parcels2.util.ext.hasBuildAnywhere
+import io.dico.parcels2.util.ext.hasPermBuildAnywhere
import org.bukkit.Location
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
@@ -37,7 +37,7 @@ interface Parcel : ParcelData {
val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
}
-interface ParcelData : AddedData {
+interface ParcelData : Privileges {
var owner: PlayerProfile?
val lastClaimTime: DateTime?
var ownerSignOutdated: Boolean
@@ -54,14 +54,15 @@ interface ParcelData : AddedData {
}
}
-class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf())
- : ParcelData, AddedDataHolder(addedMap) {
+class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
+ : ParcelData, PrivilegesHolder(addedMap) {
override var owner: PlayerProfile? = null
override var lastClaimTime: DateTime? = null
override var ownerSignOutdated = false
- override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.statusKey)
+ override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) =
+ hasPrivilegeToBuild(player)
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
- || (checkAdmin && player is Player && player.hasBuildAnywhere)
+ || (checkAdmin && player is Player && player.hasPermBuildAnywhere)
override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
index d70368f..1e9dc44 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -85,7 +85,7 @@ interface ParcelWorld : ParcelLocator, ParcelContainer {
val container: ParcelContainer
val locator: ParcelLocator
val blockManager: ParcelBlockManager
- val globalAddedData: GlobalAddedDataManager
+ val globalPrivileges: GlobalPrivilegesManager
val creationTime: DateTime?
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index c863716..3e12ba5 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -6,7 +6,7 @@ import io.dico.dicore.command.ICommandDispatcher
import io.dico.parcels2.blockvisitor.TickWorktimeLimiter
import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.command.getParcelCommands
-import io.dico.parcels2.defaultimpl.GlobalAddedDataManagerImpl
+import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl
import io.dico.parcels2.defaultimpl.ParcelProviderImpl
import io.dico.parcels2.listener.ParcelEntityTracker
import io.dico.parcels2.listener.ParcelListeners
@@ -35,7 +35,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
lateinit var options: Options; private set
lateinit var parcelProvider: ParcelProvider; private set
lateinit var storage: Storage; private set
- lateinit var globalAddedData: GlobalAddedDataManager; private set
+ lateinit var globalPrivileges: GlobalPrivilegesManager; private set
val registrator = Registrator(this)
lateinit var entityTracker: ParcelEntityTracker; private set
@@ -83,7 +83,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
return false
}
- globalAddedData = GlobalAddedDataManagerImpl(this)
+ globalPrivileges = GlobalPrivilegesManagerImpl(this)
entityTracker = ParcelEntityTracker(parcelProvider)
} catch (ex: Exception) {
plogger.error("Error loading options", ex)
diff --git a/src/main/kotlin/io/dico/parcels2/Privileges.kt b/src/main/kotlin/io/dico/parcels2/Privileges.kt
new file mode 100644
index 0000000..11a1f84
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/Privileges.kt
@@ -0,0 +1,134 @@
+package io.dico.parcels2
+
+import io.dico.parcels2.Privilege.*
+import org.bukkit.OfflinePlayer
+
+enum class Privilege(
+ val number: Int,
+ val transient: Boolean = false
+) {
+ BANNED(1),
+ DEFAULT(2),
+ CAN_BUILD(3),
+ CAN_MANAGE(4),
+
+ OWNER(-1, transient = true),
+ ADMIN(-1, transient = true);
+
+ fun requireNonTransient(): Privilege {
+ if (transient) {
+ throw IllegalArgumentException("Transient privilege $this is invalid")
+ }
+ return this
+ }
+
+ /*
+ fun canEnter() = this >= BANNED
+ fun canBuild() = this >= CAN_BUILD
+ fun canManage() = this >= CAN_MANAGE
+ */
+
+ companion object {
+ fun getByNumber(number: Int) = safeGetByNumber(number)
+ ?: throw IllegalArgumentException(
+ if (number == -1) "Transient privileges are not stored"
+ else "Privilege with number $number doesn't exist"
+ )
+
+ fun safeGetByNumber(id: Int) =
+ when (id) {
+ 1 -> BANNED
+ 2 -> DEFAULT
+ 3 -> CAN_BUILD
+ 4 -> CAN_MANAGE
+ else -> null
+ }
+ }
+}
+
+typealias PrivilegeKey = PlayerProfile.Real
+typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
+typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
+
+@Suppress("FunctionName")
+fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
+
+/**
+ * Privileges object never returns a transient privilege.
+ */
+interface Privileges {
+ val map: PrivilegeMap
+ var privilegeOfStar: Privilege
+
+ fun privilege(key: PrivilegeKey): Privilege
+ fun privilege(player: OfflinePlayer) = privilege(player.privilegeKey)
+
+ fun setPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
+ fun setPrivilege(player: OfflinePlayer, privilege: Privilege) = setPrivilege(player.privilegeKey, privilege)
+
+ fun changePrivilege(key: PrivilegeKey, expect: Privilege, update: Privilege): Boolean =
+ (when { // if CAN_BUILD is expected, CAN_MANAGE is valid.
+ expect > DEFAULT -> privilege(key) >= expect
+ expect == DEFAULT -> privilege(key) == expect
+ else -> privilege(key) <= expect
+ })
+ && setPrivilege(key, update)
+
+
+ fun hasPrivilegeToManage(key: PrivilegeKey) = privilege(key) >= CAN_MANAGE
+ fun allowManage(key: PrivilegeKey) = setPrivilege(key, CAN_MANAGE)
+ fun disallowManage(key: PrivilegeKey) = changePrivilege(key, CAN_MANAGE, CAN_BUILD)
+
+ fun hasPrivilegeToBuild(key: PrivilegeKey) = privilege(key) >= CAN_BUILD
+ fun allowBuild(key: PrivilegeKey) = setPrivilege(key, CAN_BUILD)
+ fun disallowBuild(key: PrivilegeKey) = changePrivilege(key, CAN_BUILD, DEFAULT)
+
+ fun isBanned(key: PrivilegeKey) = privilege(key) == BANNED
+ fun ban(key: PrivilegeKey) = setPrivilege(key, BANNED)
+ fun unban(key: PrivilegeKey) = changePrivilege(key, BANNED, DEFAULT)
+
+ /* OfflinePlayer overloads */
+ fun hasPrivilegeToManage(player: OfflinePlayer) = hasPrivilegeToManage(player.privilegeKey)
+
+ fun allowManage(player: OfflinePlayer) = allowManage(player.privilegeKey)
+ fun disallowManage(player: OfflinePlayer) = disallowManage(player.privilegeKey)
+
+ fun hasPrivilegeToBuild(player: OfflinePlayer) = hasPrivilegeToBuild(player.privilegeKey)
+ fun allowBuild(player: OfflinePlayer) = allowBuild(player.privilegeKey)
+ fun disallowBuild(player: OfflinePlayer) = disallowBuild(player.privilegeKey)
+
+ fun isBanned(player: OfflinePlayer) = isBanned(player.privilegeKey)
+ fun ban(player: OfflinePlayer) = ban(player.privilegeKey)
+ fun unban(player: OfflinePlayer) = unban(player.privilegeKey)
+}
+
+inline val OfflinePlayer.privilegeKey: PrivilegeKey
+ get() = PlayerProfile.nameless(this)
+
+open class PrivilegesHolder(override var map: MutablePrivilegeMap = MutablePrivilegeMap()) : Privileges {
+ override var privilegeOfStar: Privilege = DEFAULT
+ set(value) = run { field = value.requireNonTransient() }
+
+ override fun privilege(key: PrivilegeKey): Privilege = map.getOrDefault(key, privilegeOfStar)
+
+ override fun setPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ privilege.requireNonTransient()
+
+ if (key.isStar) {
+ if (privilegeOfStar == privilege) return false
+ privilegeOfStar = privilege
+ return true
+ }
+
+ return if (privilege == DEFAULT) map.remove(key) != null
+ else map.put(key, privilege) != privilege
+ }
+}
+
+interface GlobalPrivileges : Privileges {
+ val owner: PlayerProfile
+}
+
+interface GlobalPrivilegesManager {
+ operator fun get(owner: PlayerProfile): GlobalPrivileges
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
index 2339309..0c0b47f 100644
--- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
@@ -4,7 +4,7 @@ import io.dico.dicore.command.*
import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.util.ext.hasAdminManage
+import io.dico.parcels2.util.ext.hasPermAdminManage
import io.dico.parcels2.util.ext.parcelLimit
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
@@ -26,7 +26,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
}
protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
- if (player.hasAdminManage) return
+ if (player.hasPermAdminManage) return
val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
.filter { it.worldId.equals(world.id) }.size
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt
deleted file mode 100644
index 223e504..0000000
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusLocal.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.util.ext.hasAdminManage
-import org.bukkit.OfflinePlayer
-import org.bukkit.entity.Player
-
-class CommandsAddedStatusLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc("Allows a player to build on this parcel",
- shortVersion = "allows a player to build on this parcel")
- @ParcelRequire(owner = true)
- fun ParcelScope.cmdAllow(sender: Player, player: OfflinePlayer): Any? {
- Validate.isTrue(parcel.owner != null || sender.hasAdminManage, "This parcel is unowned")
- Validate.isTrue(!parcel.owner!!.matches(player), "The target already owns the parcel")
- Validate.isTrue(parcel.allow(player), "${player.name} is already allowed to build on this parcel")
- return "${player.name} is now allowed to build on this parcel"
- }
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc("Disallows a player to build on this parcel,",
- "they won't be allowed to anymore",
- shortVersion = "disallows a player to build on this parcel")
- @ParcelRequire(owner = true)
- fun ParcelScope.cmdDisallow(sender: Player, player: OfflinePlayer): Any? {
- Validate.isTrue(parcel.disallow(player), "${player.name} is not currently allowed to build on this parcel")
- return "${player.name} is not allowed to build on this parcel anymore"
- }
-
- @Cmd("ban", aliases = ["deny"])
- @Desc("Bans a player from this parcel,",
- "making them unable to enter",
- shortVersion = "bans a player from this parcel")
- @ParcelRequire(owner = true)
- fun ParcelScope.cmdBan(sender: Player, player: OfflinePlayer): Any? {
- Validate.isTrue(parcel.owner != null || sender.hasAdminManage, "This parcel is unowned")
- Validate.isTrue(!parcel.owner!!.matches(player), "The owner cannot be banned from the parcel")
- Validate.isTrue(parcel.ban(player), "${player.name} is already banned from this parcel")
- return "${player.name} is now banned from this parcel"
- }
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc("Unbans a player from this parcel,",
- "they will be able to enter it again",
- shortVersion = "unbans a player from this parcel")
- @ParcelRequire(owner = true)
- fun ParcelScope.cmdUnban(sender: Player, player: OfflinePlayer): Any? {
- Validate.isTrue(parcel.unban(player), "${player.name} is not currently banned from this parcel")
- return "${player.name} is not banned from this parcel anymore"
- }
-
-} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
index 35ede71..0b155f2 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
@@ -5,11 +5,12 @@ import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Flag
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.Privilege
class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("setowner")
- @ParcelRequire(admin = true)
+ @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdSetowner(target: PlayerProfile): Any? {
parcel.owner = target
@@ -18,14 +19,14 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
@Cmd("dispose")
- @ParcelRequire(admin = true)
+ @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdDispose(): Any? {
parcel.dispose()
return "Data of (${parcel.id.idString}) has been disposed"
}
@Cmd("reset")
- @ParcelRequire(admin = true)
+ @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
if (!sure) return areYouSureMessage(context)
parcel.dispose()
@@ -34,9 +35,10 @@ class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
@Cmd("swap")
+ @RequireParcelPrivilege(Privilege.ADMIN)
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
if (!sure) return areYouSureMessage(context)
- TODO()
+ TODO("implement swap")
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
index 3ae17f2..b646450 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
@@ -5,6 +5,7 @@ import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.annotation.Cmd
import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.Privilege
import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.doBlockOperation
import org.bukkit.Bukkit
@@ -30,7 +31,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
@Cmd("make_mess")
- @ParcelRequire(owner = true)
+ @RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
val server = plugin.server
val blockDatas = arrayOf(
@@ -47,8 +48,10 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
world.blockManager.doBlockOperation(parcel.id, traverser = RegionTraverser.upward) { block ->
block.blockData = blockDatas[random.nextInt(7)]
}.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
- context.sendMessage(EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
- .format(progress * 100, elapsedTime / 1000.0))
+ context.sendMessage(
+ EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index eef07fd..33825fb 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -9,8 +9,8 @@ import io.dico.dicore.command.annotation.RequireParameters
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.command.ParcelTarget.Kind
-import io.dico.parcels2.util.ext.hasAdminManage
import io.dico.parcels2.util.ext.hasParcelHomeOthers
+import io.dico.parcels2.util.ext.hasPermAdminManage
import io.dico.parcels2.util.ext.uuid
import org.bukkit.block.Biome
import org.bukkit.entity.Player
@@ -99,7 +99,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
)
suspend fun ParcelScope.cmdClaim(player: Player): Any? {
checkConnected("be claimed")
- parcel.owner.takeIf { !player.hasAdminManage }?.let {
+ parcel.owner.takeIf { !player.hasPermAdminManage }?.let {
error(if (it.matches(player)) "You already own this parcel" else "This parcel is not available")
}
@@ -110,14 +110,14 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
@Cmd("unclaim")
@Desc("Unclaims this parcel")
- @ParcelRequire(owner = true)
+ @RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdUnclaim(player: Player): Any? {
parcel.dispose()
return "Your parcel has been disposed"
}
@Cmd("clear")
- @ParcelRequire(owner = true)
+ @RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
if (!sure) return areYouSureMessage(context)
@@ -126,7 +126,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
@Cmd("setbiome")
- @ParcelRequire(owner = true)
+ @RequireParcelPrivilege(Privilege.OWNER)
fun ParcelScope.cmdSetbiome(context: ExecutionContext, biome: Biome): Any? {
Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
world.blockManager.setBiome(parcel.id, biome)
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsParcelOptions.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsParcelOptions.kt
deleted file mode 100644
index b46e972..0000000
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsParcelOptions.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandBuilder
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.dicore.command.annotation.RequireParameters
-import io.dico.parcels2.Parcel
-import io.dico.parcels2.ParcelsPlugin
-import org.bukkit.entity.Player
-import kotlin.reflect.KMutableProperty
-
-class CommandsParcelOptions(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
- /* TODO options
- @Cmd("inputs")
- @Desc("Sets whether players who are not allowed to",
- "build here can use levers, buttons,",
- "pressure plates, tripwire or redstone ore",
- shortVersion = "allows using inputs")
- @RequireParameters(0)
- fun ParcelScope.cmdInputs(player: Player, enabled: Boolean?): Any? {
- return runOptionCommand(player, Parcel::allowInteractInputs, enabled, "using levers, buttons, etc.")
- }
-
- @Cmd("inventory")
- @Desc("Sets whether players who are not allowed to",
- "build here can interact with inventories",
- shortVersion = "allows editing inventories")
- @RequireParameters(0)
- fun ParcelScope.cmdInventory(player: Player, enabled: Boolean?): Any? {
- return runOptionCommand(player, Parcel::allowInteractInventory, enabled, "interaction with inventories")
- }*/
-
- private inline val Boolean.enabledWord get() = if (this) "enabled" else "disabled"
- private fun ParcelScope.runOptionCommand(player: Player,
- property: KMutableProperty<Boolean>,
- enabled: Boolean?,
- desc: String): Any? {
- checkConnected("have their options changed")
- val current = property.getter.call(parcel)
- if (enabled == null) {
- val word = if (current) "" else "not "
- return "This parcel does ${word}allow $desc"
- }
-
- checkCanManage(player, "change its options")
- Validate.isTrue(current != enabled, "That option was already ${enabled.enabledWord}")
- property.setter.call(parcel, enabled)
- return "That option is now ${enabled.enabledWord}"
- }
-
- companion object {
- private const val descShort = "changes interaction options for this parcel"
- private val desc = arrayOf("Sets whether players who are not allowed to", "build here can interact with certain things.")
-
- fun setGroupDescription(builder: CommandBuilder) {
- builder.setGroupDescription(descShort, *desc)
- }
- }
-
-} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
index eae6da2..91b8272 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
@@ -3,47 +3,72 @@ package io.dico.parcels2.command
import io.dico.dicore.command.Validate
import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.GlobalAddedData
-import io.dico.parcels2.GlobalAddedDataManager
-import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.GlobalPrivileges
+import io.dico.parcels2.GlobalPrivilegesManager
import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
-class CommandsAddedStatusGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
- private inline val data get() = plugin.globalAddedData
+class CommandsPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+ private inline val data get() = plugin.globalPrivileges
@Suppress("NOTHING_TO_INLINE")
- private inline operator fun GlobalAddedDataManager.get(player: OfflinePlayer): GlobalAddedData = data[PlayerProfile(player)]
+ private inline operator fun GlobalPrivilegesManager.get(player: OfflinePlayer): GlobalPrivileges = this[PlayerProfile(player)]
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage this parcel",
+ shortVersion = "allows a player to manage this parcel"
+ )
+ fun cmdEntrust(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(player != sender, "The target cannot be yourself")
+ Validate.isTrue(data[sender].allowManage(player), "${player.name} is already allowed to manage globally")
+ return "${player.name} is now allowed to manage globally"
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ fun cmdDistrust(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(data[sender].disallowManage(player), "${player.name} is not currently allowed to manage globally")
+ return "${player.name} is not allowed to manage globally anymore"
+ }
@Cmd("allow", aliases = ["add", "permit"])
- @Desc("Globally allows a player to build on all",
+ @Desc(
+ "Globally allows a player to build on all",
"the parcels that you own.",
- shortVersion = "globally allows a player to build on your parcels")
- @ParcelRequire(owner = true)
+ shortVersion = "globally allows a player to build on your parcels"
+ )
fun cmdAllow(sender: Player, player: OfflinePlayer): Any? {
Validate.isTrue(player != sender, "The target cannot be yourself")
- Validate.isTrue(data[sender].allow(player), "${player.name} is already allowed globally")
+ Validate.isTrue(data[sender].allowBuild(player), "${player.name} is already allowed globally")
return "${player.name} is now allowed to build on all your parcels"
}
@Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc("Globally disallows a player to build on",
+ @Desc(
+ "Globally disallows a player to build on",
"the parcels that you own.",
"If the player is allowed to build on specific",
"parcels, they can still build there.",
- shortVersion = "globally disallows a player to build on your parcels")
- @ParcelRequire(owner = true)
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
fun cmdDisallow(sender: Player, player: OfflinePlayer): Any? {
Validate.isTrue(player != sender, "The target cannot be yourself")
- Validate.isTrue(data[sender].disallow(player), "${player.name} is not currently allowed globally")
+ Validate.isTrue(data[sender].disallowBuild(player), "${player.name} is not currently allowed globally")
return "${player.name} is not allowed to build on all your parcels anymore"
}
@Cmd("ban", aliases = ["deny"])
- @Desc("Globally bans a player from all the parcels",
+ @Desc(
+ "Globally bans a player from all the parcels",
"that you own, making them unable to enter.",
- shortVersion = "globally bans a player from your parcels")
- @ParcelRequire(owner = true)
+ shortVersion = "globally bans a player from your parcels"
+ )
fun cmdBan(sender: Player, player: OfflinePlayer): Any? {
Validate.isTrue(player != sender, "The target cannot be yourself")
Validate.isTrue(data[sender].ban(player), "${player.name} is already banned from all your parcels")
@@ -51,12 +76,13 @@ class CommandsAddedStatusGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(
}
@Cmd("unban", aliases = ["undeny"])
- @Desc("Globally unbans a player from all the parcels",
+ @Desc(
+ "Globally unbans a player from all the parcels",
"that you own, they can enter again.",
"If the player is banned from specific parcels,",
"they will still be banned there.",
- shortVersion = "globally unbans a player from your parcels")
- @ParcelRequire(owner = true)
+ shortVersion = "globally unbans a player from your parcels"
+ )
fun cmdUnban(sender: Player, player: OfflinePlayer): Any? {
Validate.isTrue(data[sender].unban(player), "${player.name} is not currently banned from all your parcels")
return "${player.name} is not banned from all your parcels anymore"
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
new file mode 100644
index 0000000..16b99af
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
@@ -0,0 +1,90 @@
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage this parcel",
+ shortVersion = "allows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdEntrust(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ Validate.isTrue(!parcel.owner!!.matches(player), "The target already owns the parcel")
+ Validate.isTrue(parcel.allowManage(player), "${player.name} is already allowed to manage this parcel")
+ return "${player.name} is now allowed to manage this parcel"
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage this parcel,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdDistrust(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.disallowManage(player), "${player.name} is not currently allowed to manage this parcel")
+ return "${player.name} is not allowed to manage this parcel anymore"
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Allows a player to build on this parcel",
+ shortVersion = "allows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ fun ParcelScope.cmdAllow(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ Validate.isTrue(!parcel.owner!!.matches(player), "The target already owns the parcel")
+ Validate.isTrue(parcel.allowBuild(player), "${player.name} is already allowed to build on this parcel")
+ return "${player.name} is now allowed to build on this parcel"
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Disallows a player to build on this parcel,",
+ "they won't be allowed to anymore",
+ shortVersion = "disallows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ fun ParcelScope.cmdDisallow(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.disallowBuild(player), "${player.name} is not currently allowed to build on this parcel")
+ return "${player.name} is not allowed to build on this parcel anymore"
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Bans a player from this parcel,",
+ "making them unable to enter",
+ shortVersion = "bans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ fun ParcelScope.cmdBan(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ Validate.isTrue(!parcel.owner!!.matches(player), "The owner cannot be banned from the parcel")
+ Validate.isTrue(parcel.ban(player), "${player.name} is already banned from this parcel")
+ return "${player.name} is now banned from this parcel"
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Unbans a player from this parcel,",
+ "they will be able to enter it again",
+ shortVersion = "unbans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ fun ParcelScope.cmdUnban(sender: Player, player: OfflinePlayer): Any? {
+ Validate.isTrue(parcel.unban(player), "${player.name} is not currently banned from this parcel")
+ return "${player.name} is not banned from this parcel anymore"
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
index b9dfe1e..1eddf97 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
@@ -4,6 +4,7 @@ import io.dico.dicore.command.CommandBuilder
import io.dico.dicore.command.ICommandAddress
import io.dico.dicore.command.ICommandDispatcher
import io.dico.dicore.command.registration.reflect.ReflectiveRegistration
+import io.dico.parcels2.Interactables
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.logger
import java.util.LinkedList
@@ -20,15 +21,25 @@ fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher =
group("parcel", "plot", "plots", "p") {
addRequiredPermission("parcels.command")
registerCommands(CommandsGeneral(plugin))
- registerCommands(CommandsAddedStatusLocal(plugin))
+ registerCommands(CommandsPrivilegesLocal(plugin))
group("option", "opt", "o") {
- CommandsParcelOptions.setGroupDescription(this)
- registerCommands(CommandsParcelOptions(plugin))
+ setGroupDescription(
+ "changes interaction options for this parcel",
+ "Sets whether players who are not allowed to",
+ "build here can interact with certain things."
+ )
+
+ group("interact", "i") {
+ val command = ParcelOptionsInteractCommand(plugin.parcelProvider)
+ Interactables.classesById.forEach {
+ addSubCommand(it.name, command)
+ }
+ }
}
group("global", "g") {
- registerCommands(CommandsAddedStatusGlobal(plugin))
+ registerCommands(CommandsPrivilegesGlobal(plugin))
}
group("admin", "a") {
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
index 488148d..443adfb 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
@@ -7,7 +7,9 @@ import io.dico.dicore.command.Validate
import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelWorld
-import io.dico.parcels2.util.ext.hasAdminManage
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.Privilege.*
+import io.dico.parcels2.util.ext.hasPermAdminManage
import io.dico.parcels2.util.ext.uuid
import org.bukkit.entity.Player
import java.lang.reflect.Method
@@ -18,44 +20,55 @@ import kotlin.reflect.jvm.kotlinFunction
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
-annotation class ParcelRequire(val admin: Boolean = false, val owner: Boolean = false)
+annotation class RequireParcelPrivilege(val privilege: Privilege)
+/*
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class SuspensionTimeout(val millis: Int)
+*/
open class WorldScope(val world: ParcelWorld) : ICommandReceiver
open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
- fun checkCanManage(player: Player, action: String) = Validate.isTrue(player.hasAdminManage || parcel.isOwner(player.uuid),
- "You must own this parcel to $action")
+ fun checkCanManage(player: Player, action: String) = Validate.isTrue(
+ player.hasPermAdminManage || parcel.hasPrivilegeToManage(player),
+ "You must own this parcel to $action"
+ )
}
fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
val player = context.sender as Player
val function = method.kotlinFunction!!
val receiverType = function.extensionReceiverParameter!!.type
- val require = function.findAnnotation<ParcelRequire>()
- val admin = require?.admin == true
- val owner = require?.owner == true
+ val require = function.findAnnotation<RequireParcelPrivilege>()
return when (receiverType.jvmErasure) {
- ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, admin, owner))
- WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, admin))
+ ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
+ WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
else -> throw InternalError("Invalid command receiver type")
}
}
fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
- if (admin) Validate.isTrue(player.hasAdminManage, "You must have admin rights to use that command")
+ if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
return getWorld(player.world)
?: throw CommandException("You must be in a parcel world to use that command")
}
-fun ParcelProvider.getParcelRequired(player: Player, admin: Boolean = false, own: Boolean = false): Parcel {
- val parcel = getWorldRequired(player, admin = admin).getParcelAt(player)
+fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
+ val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
?: throw CommandException("You must be in a parcel to use that command")
- if (own) Validate.isTrue(parcel.isOwner(player.uuid) || player.hasAdminManage,
- "You must own this parcel to use that command")
+
+ if (!player.hasPermAdminManage) {
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ when (privilege) {
+ OWNER ->
+ Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
+ CAN_MANAGE ->
+ Validate.isTrue(parcel.hasPrivilegeToManage(player), "You must have management privileges on this parcel to use that command")
+ }
+ }
+
return parcel
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
new file mode 100644
index 0000000..feba76e
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
@@ -0,0 +1,36 @@
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.Command
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.IContextFilter
+import io.dico.dicore.command.parameter.type.ParameterTypes
+import io.dico.parcels2.Interactables
+import io.dico.parcels2.ParcelProvider
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+class ParcelOptionsInteractCommand(val parcelProvider: ParcelProvider) : Command() {
+
+ init {
+ addContextFilter(IContextFilter.PLAYER_ONLY)
+ addParameter("allowed", "allowed", ParameterTypes.BOOLEAN)
+ }
+
+ override fun execute(sender: CommandSender, context: ExecutionContext): String? {
+ val parcel = parcelProvider.getParcelRequired(sender as Player, owner = true)
+ val interactableClassName = context.address.mainKey
+ val allowed: Boolean = context.get("allowed")
+ val change = parcel.interactableConfig.setInteractable(Interactables[interactableClassName], allowed)
+
+ return when {
+ allowed && change -> "Other players can now interact with $interactableClassName"
+ allowed && !change -> err("Other players could already interact with $interactableClassName")
+ change -> "Other players can not interact with $interactableClassName anymore"
+ else -> err("Other players were not allowed to interact with $interactableClassName")
+ }
+ }
+
+}
+
+private fun err(message: String): Nothing = throw CommandException(message) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index abf7d40..956da94 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -8,7 +8,7 @@ import io.dico.parcels2.*
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.ext.floor
-import kotlinx.coroutines.CoroutineStart.*
+import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import org.bukkit.command.CommandSender
@@ -26,12 +26,14 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef
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) {
+ 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")
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt
deleted file mode 100644
index ad7048c..0000000
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-@file:Suppress("UNCHECKED_CAST")
-
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.util.ext.alsoIfTrue
-import java.util.Collections
-
-class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager {
- private val map = mutableMapOf<PlayerProfile, GlobalAddedData>()
-
- override fun get(owner: PlayerProfile): GlobalAddedData {
- return map[owner] ?: GlobalAddedDataImpl(owner).also { map[owner] = it }
- }
-
- private inner class GlobalAddedDataImpl(override val owner: PlayerProfile,
- data: MutableAddedDataMap = emptyData)
- : AddedDataHolder(data), GlobalAddedData {
-
- private inline var data get() = addedMap; set(value) = run { addedMap = value }
- private inline val isEmpty get() = data === emptyData
-
- override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
- if (isEmpty) {
- if (status == AddedStatus.DEFAULT) return false
- data = mutableMapOf()
- }
- return super.setStatus(key, status).alsoIfTrue {
- plugin.storage.setGlobalAddedStatus(owner, key, status)
- }
- }
- }
-
- private companion object {
- val emptyData = Collections.emptyMap<Any, Any>() as MutableAddedDataMap
- }
-
-} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
new file mode 100644
index 0000000..239fe0a
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
@@ -0,0 +1,38 @@
+@file:Suppress("UNCHECKED_CAST")
+
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.util.ext.alsoIfTrue
+import java.util.Collections
+
+class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
+ private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
+
+ override fun get(owner: PlayerProfile): GlobalPrivileges {
+ return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
+ }
+
+ private inner class GlobalPrivilegesImpl(override val owner: PlayerProfile,
+ data: MutablePrivilegeMap = emptyData)
+ : PrivilegesHolder(data), GlobalPrivileges {
+
+ private inline var data get() = map; set(value) = run { map = value }
+ private inline val isEmpty get() = data === emptyData
+
+ override fun setPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ if (isEmpty) {
+ if (privilege == Privilege.DEFAULT) return false
+ data = mutableMapOf()
+ }
+ return super.set(key, privilege).alsoIfTrue {
+ plugin.storage.setGlobalPrivilege(owner, key, privilege)
+ }
+ }
+ }
+
+ private companion object {
+ val emptyData = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
index d392a8f..1580cf1 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
@@ -35,20 +35,20 @@ class ParcelImpl(
world.storage.setParcelData(this, null)
}
- override val addedMap: AddedDataMap get() = data.addedMap
- override fun getStatus(key: StatusKey) = data.getStatus(key)
- override fun isBanned(key: StatusKey) = data.isBanned(key)
- override fun isAllowed(key: StatusKey) = data.isAllowed(key)
+ override val map: PrivilegeMap get() = data.map
+ override fun privilege(key: PrivilegeKey) = data.privilege(key)
+ override fun isBanned(key: PrivilegeKey) = data.isBanned(key)
+ override fun hasPrivilegeToBuild(key: PrivilegeKey) = data.hasPrivilegeToBuild(key)
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean): Boolean {
return (data.canBuild(player, checkAdmin, false))
- || checkGlobal && world.globalAddedData[owner ?: return false].isAllowed(player)
+ || checkGlobal && world.globalPrivileges[owner ?: return false].hasPrivilegeToBuild(player)
}
- override var statusOfStar: AddedStatus
- get() = data.statusOfStar
- set(value) = run { setStatus(PlayerProfile.Star, value) }
+ override var privilegeOfStar: Privilege
+ get() = data.privilegeOfStar
+ set(value) = run { setPrivilege(PlayerProfile.Star, value) }
- val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap }
+ val globalAddedMap: PrivilegeMap? get() = owner?.let { world.globalPrivileges[it].map }
override val lastClaimTime: DateTime? get() = data.lastClaimTime
@@ -71,12 +71,16 @@ class ParcelImpl(
}
}
- override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
- return data.setStatus(key, status).alsoIfTrue {
- world.storage.setParcelPlayerStatus(this, key, status)
+ override fun setPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ return data.setPrivilege(key, privilege).alsoIfTrue {
+ world.storage.setLocalPrivilege(this, key, privilege)
}
}
+ private fun updateInteractableConfigStorage() {
+ world.storage.setParcelOptionsInteractConfig(this, data.interactableConfig)
+ }
+
private var _interactableConfig: InteractableConfiguration? = null
override var interactableConfig: InteractableConfiguration
get() {
@@ -86,21 +90,18 @@ class ParcelImpl(
override fun isInteractable(clazz: Interactables): Boolean = data.interactableConfig.isInteractable(clazz)
override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean =
- data.interactableConfig.setInteractable(clazz, interactable).alsoIfTrue {
- // TODO update storage
- }
+ data.interactableConfig.setInteractable(clazz, interactable).alsoIfTrue { updateInteractableConfigStorage() }
override fun clear(): Boolean =
- data.interactableConfig.clear().alsoIfTrue {
- // TODO update storage
- }
+ data.interactableConfig.clear().alsoIfTrue { updateInteractableConfigStorage() }
}
}
return _interactableConfig!!
}
set(value) {
- data.interactableConfig.copyFrom(value)
- // TODO update storage
+ if (data.interactableConfig.copyFrom(value)) {
+ updateInteractableConfigStorage()
+ }
}
private var blockVisitors = AtomicInteger(0)
@@ -139,10 +140,10 @@ private object ParcelInfoStringComputer {
append(' ')
}
- private fun StringBuilder.appendAddedList(local: AddedDataMap, global: AddedDataMap, status: AddedStatus, fieldName: String) {
+ private fun StringBuilder.appendAddedList(local: PrivilegeMap, global: PrivilegeMap, status: Privilege, fieldName: String) {
val globalSet = global.filterValues { it == status }.keys
val localList = local.filterValues { it == status }.keys.filter { it !in globalSet }
- val stringList = globalSet.map(StatusKey::notNullName).map { "(G)$it" } + localList.map(StatusKey::notNullName)
+ val stringList = globalSet.map(PrivilegeKey::notNullName).map { "(G)$it" } + localList.map(PrivilegeKey::notNullName)
if (stringList.isEmpty()) return
appendField({
@@ -182,11 +183,11 @@ private object ParcelInfoStringComputer {
append('\n')
- val global = owner?.let { parcel.world.globalAddedData[owner].addedMap } ?: emptyMap()
- val local = parcel.addedMap
- appendAddedList(local, global, AddedStatus.ALLOWED, "Allowed")
+ val global = owner?.let { parcel.world.globalPrivileges[owner].map } ?: emptyMap()
+ val local = parcel.map
+ appendAddedList(local, global, Privilege.CAN_BUILD, "Allowed")
append('\n')
- appendAddedList(local, global, AddedStatus.BANNED, "Banned")
+ appendAddedList(local, global, Privilege.BANNED, "Banned")
/* TODO options
if (!parcel.allowInteractInputs || !parcel.allowInteractInventory) {
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
index 8920e2e..1112047 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
@@ -2,8 +2,6 @@ package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import io.dico.parcels2.util.schedule
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.Unconfined
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
@@ -61,7 +59,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") }
parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage,
- plugin.globalAddedData, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter)
+ plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter)
if (!worldExists) {
val time = DateTime.now()
@@ -119,7 +117,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
worldOptions,
worldOptions.generator.newGenerator(this, worldName),
plugin.storage,
- plugin.globalAddedData,
+ plugin.globalPrivileges,
::DefaultParcelContainer)
} catch (ex: Exception) {
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
index 9f96a8c..519008c 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
@@ -15,7 +15,7 @@ class ParcelWorldImpl(override val world: World,
override val generator: ParcelGenerator,
override var options: RuntimeWorldOptions,
override val storage: Storage,
- override val globalAddedData: GlobalAddedDataManager,
+ val globalPrivileges: GlobalPrivilegesManager,
containerFactory: ParcelContainerFactory,
coroutineScope: CoroutineScope,
worktimeLimiter: WorktimeLimiter)
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
index 1af3406..c8e64d3 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
@@ -36,7 +36,7 @@ class ParcelListeners(
val entityTracker: ParcelEntityTracker,
val storage: Storage
) {
- private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasBuildAnywhere
+ private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasPermBuildAnywhere
/**
* Get the world and parcel that the block resides in
@@ -55,9 +55,9 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
val user = event.player
- if (user.hasBanBypass) return@l
+ if (user.hasPermBanBypass) return@l
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
- if (parcel.isBanned(user.statusKey)) {
+ if (parcel.isBanned(user.privilegeKey)) {
parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.homeLocation)
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
@@ -72,7 +72,7 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onBlockBreakEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
- if (!event.player.hasBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
+ if (!event.player.hasPermBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
event.isCancelled = true; return@l
}
@@ -91,7 +91,7 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onBlockPlaceEvent = RegistratorListener<BlockPlaceEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
- if (!event.player.hasBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
+ if (!event.player.hasPermBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
event.isCancelled = true
}
}
@@ -185,7 +185,7 @@ class ParcelListeners(
val clickedBlock = event.clickedBlock
val parcel = clickedBlock?.let { world.getParcelAt(it) }
- if (!user.hasBuildAnywhere && parcel.isPresentAnd { isBanned(user.statusKey) }) {
+ if (!user.hasPermBuildAnywhere && parcel.isPresentAnd { isBanned(user.privilegeKey) }) {
user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
event.isCancelled = true; return@l
}
@@ -218,7 +218,7 @@ class ParcelListeners(
}
Action.RIGHT_CLICK_AIR -> onPlayerInteractEvent_RightClick(event, world, parcel)
- Action.PHYSICAL -> if (!user.hasBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || interactableConfig("pressure_plates") }) {
+ Action.PHYSICAL -> if (!user.hasPermBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || interactableConfig("pressure_plates") }) {
user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
event.isCancelled = true; return@l
}
@@ -484,7 +484,7 @@ class ParcelListeners(
event.isCancelled = true; return@l
}
- if (!event.player.hasBuildAnywhere && !ppa.canBuild(event.player)) {
+ if (!event.player.hasPermBuildAnywhere && !ppa.canBuild(event.player)) {
event.isCancelled = true; return@l
}
@@ -560,7 +560,7 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onPlayerChangedWorldEvent = RegistratorListener<PlayerChangedWorldEvent> l@{ event ->
val world = parcelProvider.getWorld(event.player.world) ?: return@l
- if (world.options.gameMode != null && !event.player.hasGamemodeBypass) {
+ if (world.options.gameMode != null && !event.player.hasPermGamemodeBypass) {
event.player.gameMode = world.options.gameMode
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
index b68f7c2..b279d2d 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
@@ -4,7 +4,6 @@ import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER
import com.sk89q.worldedit.Vector
import com.sk89q.worldedit.Vector2D
import com.sk89q.worldedit.WorldEdit
-import com.sk89q.worldedit.WorldEditException
import com.sk89q.worldedit.bukkit.WorldEditPlugin
import com.sk89q.worldedit.event.extent.EditSessionEvent
import com.sk89q.worldedit.extent.AbstractDelegateExtent
@@ -12,11 +11,10 @@ import com.sk89q.worldedit.extent.Extent
import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY
import com.sk89q.worldedit.util.eventbus.Subscribe
import com.sk89q.worldedit.world.biome.BaseBiome
-import com.sk89q.worldedit.world.block.BaseBlock
import com.sk89q.worldedit.world.block.BlockStateHolder
import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.util.ext.hasBuildAnywhere
+import io.dico.parcels2.util.ext.hasPermBuildAnywhere
import io.dico.parcels2.util.ext.sendParcelMessage
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
@@ -33,7 +31,7 @@ class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) {
if (actor == null || !actor.isPlayer) return
val player = parcels.server.getPlayer(actor.uniqueId)
- if (player.hasBuildAnywhere) return
+ if (player.hasPermBuildAnywhere) return
event.extent = ParcelsExtent(event.extent, world, player)
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
index 6c91714..69c5b62 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
@@ -1,13 +1,13 @@
package io.dico.parcels2.storage
import io.dico.parcels2.*
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel
import org.joda.time.DateTime
import java.util.UUID
+import kotlin.coroutines.CoroutineContext
interface Backing {
@@ -15,7 +15,7 @@ interface Backing {
val isConnected: Boolean
- val dispatcher: CoroutineDispatcher
+ val coroutineContext: CoroutineContext
fun launchJob(job: Backing.() -> Unit): Job
@@ -56,9 +56,9 @@ interface Backing {
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
- fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, status: Privilege)
- fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?)
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
/*
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)
@@ -67,7 +67,7 @@ interface Backing {
fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>)
- fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap
+ fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap
- fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus)
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege)
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
index c9c0d4a..fa63496 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
@@ -3,6 +3,7 @@
package io.dico.parcels2.storage
import io.dico.parcels2.*
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ReceiveChannel
@@ -10,9 +11,10 @@ import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.launch
import org.joda.time.DateTime
import java.util.UUID
+import kotlin.coroutines.CoroutineContext
typealias DataPair = Pair<ParcelId, ParcelData?>
-typealias AddedDataPair<TAttach> = Pair<TAttach, MutableAddedDataMap>
+typealias AddedDataPair<TAttach> = Pair<TAttach, MutablePrivilegeMap>
interface Storage {
val name: String
@@ -48,28 +50,29 @@ interface Storage {
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
- fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
- fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray): Job
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>>
- fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?>
+ fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?>
- fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus): Job
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege): Job
fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>>
}
-class BackedStorage internal constructor(val b: Backing) : Storage {
+class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
override val name get() = b.name
override val isConnected get() = b.isConnected
+ override val coroutineContext: CoroutineContext get() = b.coroutineContext
- override fun init() = launch(b.dispatcher) { b.init() }
+ override fun init() = launch { b.init() }
- override fun shutdown() = launch(b.dispatcher) { b.shutdown() }
+ override fun shutdown() = launch { b.shutdown() }
override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
@@ -96,16 +99,16 @@ class BackedStorage internal constructor(val b: Backing) : Storage {
override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
- override fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setLocalPlayerStatus(parcel, player, status) }
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
- override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray) = b.launchJob { b.setParcelOptionsInteractBitmask(parcel, bitmask) }
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
override fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalAddedData(it) }
- override fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?> = b.launchFuture { b.readGlobalAddedData(owner) }
+ override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?> = b.launchFuture { b.readGlobalPrivileges(owner) }
- override fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setGlobalPlayerStatus(owner, player, status) }
+ override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, status) }
override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
}
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 b7f9f82..4c88c11 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -23,17 +23,16 @@ import javax.sql.DataSource
class ExposedDatabaseException(message: String? = null) : Exception(message)
-class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing {
+class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing, CoroutineScope {
override val name get() = "Exposed"
- override val dispatcher: ThreadPoolDispatcher = newFixedThreadPoolContext(poolSize, "Parcels StorageThread")
-
+ override val coroutineContext = Job() + newFixedThreadPoolContext(poolSize, "Parcels StorageThread")
private var dataSource: DataSource? = null
private var database: Database? = null
private var isShutdown: Boolean = false
override val isConnected get() = database != null
- override fun launchJob(job: Backing.() -> Unit): Job = launch(dispatcher) { transaction { job() } }
- override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async(dispatcher) { transaction { future() } }
+ override fun launchJob(job: Backing.() -> Unit): Job = launch { transaction { job() } }
+ override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async { transaction { future() } }
override fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T> {
val channel = LinkedListChannel<T>()
@@ -44,8 +43,8 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
override fun <T> openChannelForWriting(action: Backing.(T) -> Unit): SendChannel<T> {
val channel = ArrayChannel<T>(poolSize * 2)
- repeat(poolSize) {
- launch(dispatcher) {
+ repeat(poolSize.clampMax(3)) {
+ launch {
try {
while (true) {
action(channel.receive())
@@ -75,7 +74,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
dataSource = dataSourceFactory()
database = Database.connect(dataSource!!)
transaction(database!!) {
- create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, AddedLocalT, AddedGlobalT)
+ create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT)
}
}
}
@@ -83,11 +82,12 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
override fun shutdown() {
synchronized {
if (isShutdown) throw IllegalStateException()
+ isShutdown = true
+ coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown"))
dataSource?.let {
(it as? HikariDataSource)?.close()
}
database = null
- isShutdown = true
}
}
@@ -173,7 +173,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
// Below should cascade automatically
/*
- AddedLocalT.deleteIgnoreWhere { AddedLocalT.parcel_id eq id }
+ PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.parcel_id eq id }
ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id }
*/
}
@@ -184,18 +184,16 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
transaction {
val id = ParcelsT.getOrInitId(parcel)
- AddedLocalT.deleteIgnoreWhere { AddedLocalT.attach_id eq id }
+ PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.attach_id eq id }
}
setParcelOwner(parcel, data.owner)
- for ((profile, status) in data.addedMap) {
- AddedLocalT.setPlayerStatus(parcel, profile, status)
+ for ((profile, privilege) in data.map) {
+ PrivilegesLocalT.setPrivilege(parcel, profile, privilege)
}
- val bitmaskArray = (data.interactableConfig as? BitmaskInteractableConfiguration ?: return).bitmaskArray
- val isAllZero = bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 }
- setParcelOptionsInteractBitmask(parcel, if (isAllZero) null else bitmaskArray)
+ setParcelOptionsInteractConfig(parcel, data.interactableConfig)
}
override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) {
@@ -221,19 +219,22 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
}
}
- override fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) {
- AddedLocalT.setPlayerStatus(parcel, player.toRealProfile(), status)
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) {
+ PrivilegesLocalT.setPrivilege(parcel, player.toRealProfile(), privilege)
}
- override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?) {
- if (bitmask == null) {
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) {
+ val bitmaskArray = (config as? BitmaskInteractableConfiguration ?: return).bitmaskArray
+ val isAllZero = !bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 }
+
+ if (isAllZero) {
val id = ParcelsT.getId(parcel) ?: return
ParcelOptionsT.deleteWhere { ParcelOptionsT.parcel_id eq id }
return
}
- if (bitmask.size != 1) throw IllegalArgumentException()
- val array = bitmask.toByteArray()
+ if (bitmaskArray.size != 1) throw IllegalArgumentException()
+ val array = bitmaskArray.toByteArray()
val id = ParcelsT.getOrInitId(parcel)
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
it[parcel_id] = id
@@ -242,16 +243,16 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
}
override fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) {
- AddedGlobalT.sendAllAddedData(channel)
+ PrivilegesGlobalT.sendAllAddedData(channel)
channel.close()
}
- override fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap {
- return AddedGlobalT.readAddedData(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf())
+ override fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap {
+ return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf())
}
- override fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) {
- AddedGlobalT.setPlayerStatus(owner, player.toRealProfile(), status)
+ override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) {
+ PrivilegesGlobalT.setPrivilege(owner, player.toRealProfile(), privilege)
}
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
@@ -266,7 +267,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size))
}
- addedMap = AddedLocalT.readAddedData(id)
+ map = PrivilegesLocalT.readPrivileges(id)
}
}
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 696b84c..6f6ad6b 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -26,7 +26,7 @@ abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj
internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
return getId() ?: table.insertIgnore(body)[id] ?: getId()
- ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its id")
+ ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
}
abstract fun getId(obj: QueryObj): Int?
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 f41d545..600255e 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
@@ -3,30 +3,30 @@
package io.dico.parcels2.storage.exposed
import io.dico.parcels2.*
-import io.dico.parcels2.AddedStatus.ALLOWED
-import io.dico.parcels2.AddedStatus.DEFAULT
+import io.dico.parcels2.Privilege.DEFAULT
import kotlinx.coroutines.channels.SendChannel
import org.jetbrains.exposed.sql.*
-import java.util.UUID
-object AddedLocalT : AddedTable<ParcelId>("parcels_added_local", ParcelsT)
-object AddedGlobalT : AddedTable<PlayerProfile>("parcels_added_global", ProfilesT)
+object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_added_local", ParcelsT)
+object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_added_global", ProfilesT)
object ParcelOptionsT : Table("parcel_options") {
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
- val interact_bitmask = binary("interact_bitmask", 4).default(ByteArray(4) { 0 }) // all zero by default
+ val interact_bitmask = binary("interact_bitmask", 4)
}
-typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableAddedDataMap>>
+typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, MutablePrivilegeMap>>
-sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
+sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
- val allowed_flag = bool("allowed_flag")
+ val privilege = integer("privilege")
val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
- fun setPlayerStatus(attachedOn: AttachT, player: PlayerProfile.Real, status: AddedStatus) {
- if (status == DEFAULT) {
+ fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
+ privilege.requireNonTransient()
+
+ if (privilege == DEFAULT) {
val player_id = ProfilesT.getId(player) ?: return
idTable.getId(attachedOn)?.let { holder ->
deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
@@ -39,28 +39,28 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<
upsert(conflictIndex = index_pair) {
it[attach_id] = holder
it[profile_id] = player_id
- it[allowed_flag] = status == ALLOWED
+ it[this.privilege] = privilege.number
}
}
- fun readAddedData(id: Int): MutableAddedDataMap {
- val list = slice(profile_id, allowed_flag).select { attach_id eq id }
- val result = MutableAddedDataMap()
+ fun readPrivileges(id: Int): MutablePrivilegeMap {
+ val list = slice(profile_id, privilege).select { attach_id eq id }
+ val result = MutablePrivilegeMap()
for (row in list) {
val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
- result[profile] = row[allowed_flag].asAddedStatus()
+ result[profile] = Privilege.safeGetByNumber(row[privilege]) ?: continue
}
return result
}
- fun sendAllAddedData(channel: AddedStatusSendChannel<AttachT>) {
+ fun sendAllAddedData(channel: PrivilegesSendChannel<AttachT>) {
val iterator = selectAll().orderBy(attach_id).iterator()
if (iterator.hasNext()) {
val firstRow = iterator.next()
var id: Int = firstRow[attach_id]
var attach: AttachT? = null
- var map: MutableAddedDataMap? = null
+ var map: MutablePrivilegeMap? = null
fun initAttachAndMap() {
attach = idTable.getItem(id)
@@ -90,14 +90,12 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<
}
val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
- val status = row[allowed_flag].asAddedStatus()
- map!![profile] = status
+ val privilege = Privilege.safeGetByNumber(row[privilege]) ?: continue
+ map!![profile] = privilege
}
sendIfPresent()
}
}
- private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) ALLOWED else AddedStatus.BANNED
-
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
index 0dcf36d..b514f87 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
@@ -66,11 +66,11 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
return ParcelId(world, row[table.px], row[table.pz])
}
- fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: AddedStatus) {
+ fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
selectAll().forEach { row ->
val parcel = getParcelId(this, row) ?: return@forEach
val profile = StatusKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
- target.setParcelPlayerStatus(parcel, profile, kind)
+ target.setLocalPrivilege(parcel, profile, kind)
}
}
@@ -89,12 +89,12 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
mlogger.info("Transmitting data from plotmeallowed table")
transaction {
- PlotmeAllowedT.transmitPlotmeAddedTable(AddedStatus.ALLOWED)
+ PlotmeAllowedT.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
}
mlogger.info("Transmitting data from plotmedenied table")
transaction {
- PlotmeDeniedT.transmitPlotmeAddedTable(AddedStatus.BANNED)
+ PlotmeDeniedT.transmitPlotmeAddedTable(Privilege.BANNED)
}
mlogger.warn("Data has been **transmitted**.")
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
index 38402f0..83bcaf2 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
@@ -13,12 +13,12 @@ inline val OfflinePlayer.uuid get() = uniqueId
inline val OfflinePlayer.isValid
get() = isOnline() || hasPlayedBefore()
-inline val Player.hasBanBypass get() = hasPermission("parcels.admin.bypass.ban")
-inline val Player.hasGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
-inline val Player.hasBuildAnywhere get() = hasPermission("parcels.admin.bypass.build")
-inline val Player.hasAdminManage get() = hasPermission("parcels.admin.manage")
+inline val Player.hasPermBanBypass get() = hasPermission("parcels.admin.bypass.ban")
+inline val Player.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
+inline val Player.hasPermBuildAnywhere get() = hasPermission("parcels.admin.bypass.build")
+inline val Player.hasPermAdminManage get() = hasPermission("parcels.admin.manage")
inline val Player.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
-inline val Player.hasRandomSpecific get() = hasPermission("parcels.command.random.specific")
+inline val Player.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
val Player.parcelLimit: Int
get() {
for (info in effectivePermissions) {