Replace AddedData API with Privileges API, adding CAN_MANAGE and required changes
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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])
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
134
src/main/kotlin/io/dico/parcels2/Privileges.kt
Normal file
134
src/main/kotlin/io/dico/parcels2/Privileges.kt
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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") {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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**.")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user