Archived
0

Cleanup of privilege and listeners

This commit is contained in:
Dico
2018-09-24 08:38:25 +01:00
parent a61e1d69b2
commit 68a0bb0539
13 changed files with 278 additions and 450 deletions

View File

@@ -1,10 +1,7 @@
package io.dico.parcels2
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.ext.hasPermBuildAnywhere
import org.bukkit.Location
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.joda.time.DateTime
import java.util.UUID
@@ -16,7 +13,7 @@ import java.util.UUID
* However, this implementation is intentionally not thread-safe.
* Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
*/
interface Parcel : ParcelData {
interface Parcel : ParcelData, Privileges {
val id: ParcelId
val world: ParcelWorld
val pos: Vec2i
@@ -25,6 +22,10 @@ interface Parcel : ParcelData {
val data: ParcelData
val infoString: String
val hasBlockVisitors: Boolean
val globalPrivileges: GlobalPrivileges?
override val keyOfOwner: PlayerProfile.Real?
get() = owner as? PlayerProfile.Real
fun copyDataIgnoringDatabase(data: ParcelData)
@@ -37,13 +38,13 @@ interface Parcel : ParcelData {
val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
}
interface ParcelData : Privileges {
interface ParcelData : PrivilegesMinimal {
var owner: PlayerProfile?
val lastClaimTime: DateTime?
var ownerSignOutdated: Boolean
var interactableConfig: InteractableConfiguration
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
//fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
fun isOwner(uuid: UUID): Boolean {
return owner?.uuid == uuid
@@ -59,10 +60,11 @@ class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
override var owner: PlayerProfile? = null
override var lastClaimTime: DateTime? = null
override var ownerSignOutdated = false
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.hasPermBuildAnywhere)
//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.hasPermBuildAnywhere)
override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
}

View File

@@ -0,0 +1,146 @@
package io.dico.parcels2
import io.dico.parcels2.Privilege.*
import io.dico.parcels2.PrivilegeChangeResult.*
import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
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 isDistanceGrEq(other: Privilege): Boolean =
when { // used for example when disallowBuild is called and CAN_MANAGE is the privilege.
other > DEFAULT -> this >= other
other == DEFAULT -> this == other
else -> this <= other
}
fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
if (positiveDirection) update > this
else update < this
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(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()
interface PrivilegesMinimal {
val map: PrivilegeMap
var privilegeOfStar: Privilege
fun getStoredPrivilege(key: PrivilegeKey): Privilege
fun setStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
}
interface Privileges : PrivilegesMinimal {
val keyOfOwner: PlayerProfile.Real?
fun privilege(player: OfflinePlayer, adminPerm: String): Privilege =
if (player is Player && player.hasPermission(adminPerm)) ADMIN
else {
val key = player.privilegeKey
if (key == keyOfOwner) OWNER
else getStoredPrivilege(key)
}
fun changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
if (key != keyOfOwner) FAIL_OWNER
else if (getStoredPrivilege(key).isChangeInDirection(positive, update)
&& setStoredPrivilege(key, update)
) SUCCESS
else FAIL
fun canManage(player: OfflinePlayer) = privilege(player, PERM_ADMIN_MANAGE) >= CAN_MANAGE
fun allowManage(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, CAN_MANAGE)
fun disallowManage(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, CAN_BUILD)
fun canBuild(player: OfflinePlayer) = privilege(player, PERM_BUILD_ANYWHERE) >= CAN_BUILD
fun allowBuild(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, CAN_BUILD)
fun disallowBuild(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, DEFAULT)
fun canEnter(player: OfflinePlayer) = privilege(player, PERM_BAN_BYPASS) >= DEFAULT
fun ban(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, BANNED)
fun unban(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, DEFAULT)
/**
* same as [canBuild] but doesn't perform a permission check for admin perms
*/
fun canBuildFast(player: OfflinePlayer) = player.privilegeKey.let { if (it == keyOfOwner) OWNER else getStoredPrivilege(it)} >= CAN_BUILD
}
enum class PrivilegeChangeResult {
SUCCESS, FAIL, FAIL_OWNER
}
val OfflinePlayer.privilegeKey: PrivilegeKey
inline get() = PlayerProfile.nameless(this)
open class PrivilegesHolder(override var map: MutablePrivilegeMap = MutablePrivilegeMap()) : PrivilegesMinimal {
override var privilegeOfStar: Privilege = DEFAULT
set(value) = run { field = value.requireNonTransient() }
override fun getStoredPrivilege(key: PrivilegeKey) =
if (key.isStar) privilegeOfStar
else map.getOrDefault(key, privilegeOfStar)
override fun setStoredPrivilege(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 {
override val keyOfOwner: PlayerProfile.Real
}
interface GlobalPrivilegesManager {
operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
}

View File

@@ -1,128 +0,0 @@
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 isDistanceGrEq(other: Privilege): Boolean =
when { // used for example when disallowBuild is called and CAN_MANAGE is the privilege.
other > DEFAULT -> this >= other
other == DEFAULT -> this == other
else -> this <= other
}
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(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 =
privilege(key).isDistanceGrEq(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)
}
val OfflinePlayer.privilegeKey: PrivilegeKey
inline 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
operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
}

View File

@@ -20,9 +20,8 @@ import org.bukkit.block.Biome
import org.bukkit.block.BlockFace
import org.bukkit.block.Skull
import org.bukkit.block.data.BlockData
import org.bukkit.block.data.type.Sign
import org.bukkit.block.data.type.Slab
import java.lang.IllegalArgumentException
import org.bukkit.block.data.type.WallSign
import java.util.Random
private val airType = Bukkit.createBlockData(Material.AIR)
@@ -201,8 +200,7 @@ class DefaultParcelGenerator(
o.wallType
wallBlock.blockData = wallBlockType
signBlock.blockData = (Bukkit.createBlockData(Material.WALL_SIGN) as Sign).apply { rotation = BlockFace.NORTH }
signBlock.blockData = (Bukkit.createBlockData(Material.WALL_SIGN) as WallSign).apply { facing = BlockFace.NORTH }
val sign = signBlock.state as org.bukkit.block.Sign
sign.setLine(0, "${parcel.x},${parcel.z}")

View File

@@ -9,25 +9,25 @@ import java.util.Collections
class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
override fun get(owner: PlayerProfile): GlobalPrivileges {
override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
}
private inner class GlobalPrivilegesImpl(
override val owner: PlayerProfile,
override val keyOfOwner: PlayerProfile.Real,
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 {
override fun setStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
if (isEmpty) {
if (privilege == Privilege.DEFAULT) return false
data = mutableMapOf()
}
return super<PrivilegesHolder>.setPrivilege(key, privilege).alsoIfTrue {
plugin.storage.setGlobalPrivilege(owner, key, privilege)
return super.setStoredPrivilege(key, privilege).alsoIfTrue {
plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
}
}
}

View File

@@ -2,6 +2,7 @@ package io.dico.parcels2.defaultimpl
import io.dico.dicore.Formatting
import io.dico.parcels2.*
import io.dico.parcels2.Privilege.*
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.ext.alsoIfTrue
import org.bukkit.Material
@@ -36,19 +37,26 @@ class ParcelImpl(
}
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.globalPrivileges[owner ?: return false].hasPrivilegeToBuild(player)
override fun getStoredPrivilege(key: PrivilegeKey) = data.getStoredPrivilege(key)
override fun setStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
return data.setStoredPrivilege(key, privilege).alsoIfTrue {
world.storage.setLocalPrivilege(this, key, privilege)
}
}
override fun privilege(player: OfflinePlayer, adminPerm: String): Privilege {
val privilege = super.privilege(player, adminPerm)
return if (privilege == DEFAULT) globalPrivileges?.privilege(player, adminPerm) ?: DEFAULT
else privilege
}
override var privilegeOfStar: Privilege
get() = data.privilegeOfStar
set(value) = run { setPrivilege(PlayerProfile.Star, value) }
get() = data.privilegeOfStar.let { if (it == DEFAULT) globalPrivileges?.privilegeOfStar ?: DEFAULT else it }
set(value) = run { setStoredPrivilege(PlayerProfile.Star, value) }
val globalAddedMap: PrivilegeMap? get() = owner?.let { world.globalPrivileges[it].map }
override val globalPrivileges: GlobalPrivileges?
get() = keyOfOwner?.let { world.globalPrivileges[it] }
override val lastClaimTime: DateTime? get() = data.lastClaimTime
@@ -71,12 +79,6 @@ class ParcelImpl(
}
}
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)
}
@@ -188,12 +190,14 @@ private object ParcelInfoStringComputer {
val (key, priv) = pair
// prefix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
append(when {
global && priv == Privilege.CAN_MANAGE -> "(GT)"
global -> "(G)"
priv == Privilege.CAN_MANAGE -> "(T)"
else -> ""
})
append(
when {
global && priv == CAN_MANAGE -> "(GT)"
global -> "(G)"
priv == CAN_MANAGE -> "(T)"
else -> ""
}
)
append(key.notNullName)
}
@@ -219,11 +223,11 @@ private object ParcelInfoStringComputer {
append('\n')
val global = owner?.let { parcel.world.globalPrivileges[owner].map } ?: emptyMap()
val local = parcel.map
appendAddedList(local, global, Privilege.CAN_BUILD, "Allowed") // includes CAN_MANAGE privilege
val global = parcel.globalPrivileges?.map ?: emptyMap()
appendAddedList(local, global, CAN_BUILD, "Allowed") // includes CAN_MANAGE privilege
append('\n')
appendAddedList(local, global, Privilege.BANNED, "Banned")
appendAddedList(local, global, BANNED, "Banned")
if (!parcel.interactableConfig.isDefault()) {
val interactables = parcel.interactableConfig.interactableClasses

View File

@@ -1,6 +1,7 @@
package io.dico.parcels2.listener
import gnu.trove.TLongCollection
import gnu.trove.set.hash.TLongHashSet
import io.dico.dicore.ListenerMarker
import io.dico.dicore.RegistratorListener
import io.dico.parcels2.*
@@ -30,21 +31,23 @@ import org.bukkit.event.world.StructureGrowEvent
import org.bukkit.inventory.InventoryHolder
import java.util.EnumSet
@Suppress("NOTHING_TO_INLINE")
class ParcelListeners(
val parcelProvider: ParcelProvider,
val entityTracker: ParcelEntityTracker,
val storage: Storage
) {
private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasPermBuildAnywhere
private fun canBuildOnArea(user: Player, area: Parcel?) =
if (area == null) user.hasPermBuildAnywhere else area.canBuild(user)
private fun canInteract(user: Player, area: Parcel?, interactClass: String) =
canBuildOnArea(user, area) || (area != null && area.interactableConfig(interactClass))
/**
* Get the world and parcel that the block resides in
* wo is the world, ppa is the parcel
* ppa for possibly a parcel - it will be null if not in an existing parcel
* returns null if not in a registered parcel world
* the parcel is nullable, and often named area because that means path.
* returns null if not in a registered parcel world - should always return in that case to not affect other worlds.
*/
private fun getWoAndPPa(block: Block): Pair<ParcelWorld, Parcel?>? {
private fun getWorldAndArea(block: Block): Pair<ParcelWorld, Parcel?>? {
val world = parcelProvider.getWorld(block.world) ?: return null
return world to world.getParcelAt(block)
}
@@ -57,7 +60,7 @@ class ParcelListeners(
val user = event.player
if (user.hasPermBanBypass) return@l
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
if (parcel.isBanned(user.privilegeKey)) {
if (!parcel.canEnter(user)) {
parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.homeLocation)
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
@@ -71,12 +74,12 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onBlockBreakEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
if (!event.player.hasPermBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
val (world, area) = getWorldAndArea(event.block) ?: return@l
if (!canBuildOnArea(event.player, area)) {
event.isCancelled = true; return@l
}
if (!wo.options.dropEntityItems) {
if (!world.options.dropEntityItems) {
val state = event.block.state
if (state is InventoryHolder) {
state.inventory.clear()
@@ -90,8 +93,8 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onBlockPlaceEvent = RegistratorListener<BlockPlaceEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
if (!event.player.hasPermBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
val (_, area) = getWorldAndArea(event.block) ?: return@l
if (!canBuildOnArea(event.player, area)) {
event.isCancelled = true
}
}
@@ -120,7 +123,7 @@ class ParcelListeners(
private fun checkPistonMovement(event: BlockPistonEvent, blocks: List<Block>) {
val world = parcelProvider.getWorld(event.block.world) ?: return
val direction = event.direction
val columns = gnu.trove.set.hash.TLongHashSet(blocks.size * 2)
val columns = TLongHashSet(blocks.size * 2)
blocks.forEach {
columns.add(Column(it.x, it.z))
@@ -128,8 +131,8 @@ class ParcelListeners(
}
columns.troveForEach {
val ppa = world.getParcelAt(it.columnX, it.columnZ)
if (ppa.isNullOr { hasBlockVisitors }) {
val area = world.getParcelAt(it.columnX, it.columnZ)
if (area == null || area.hasBlockVisitors) {
event.isCancelled = true
return
}
@@ -141,10 +144,10 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onExplosionPrimeEvent = RegistratorListener<ExplosionPrimeEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.entity.location.block) ?: return@l
if (ppa?.hasBlockVisitors == true) {
val (world, area) = getWorldAndArea(event.entity.location.block) ?: return@l
if (area != null && area.hasBlockVisitors) {
event.radius = 0F; event.isCancelled = true
} else if (wo.options.disableExplosions) {
} else if (world.options.disableExplosions) {
event.radius = 0F
}
}
@@ -156,18 +159,18 @@ class ParcelListeners(
val onEntityExplodeEvent = RegistratorListener<EntityExplodeEvent> l@{ event ->
entityTracker.untrack(event.entity)
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
if (world.options.disableExplosions || world.getParcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
if (world.options.disableExplosions || world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
event.isCancelled = true
}
}
/*
* Prevents creepers and tnt minecarts from exploding if explosions are disabled
* Prevents liquids from flowing out of plots
*/
@field:ListenerMarker(priority = NORMAL)
val onBlockFromToEvent = RegistratorListener<BlockFromToEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.toBlock) ?: return@l
if (ppa.isNullOr { hasBlockVisitors }) event.isCancelled = true
val (_, area) = getWorldAndArea(event.toBlock) ?: return@l
if (area == null || area.hasBlockVisitors) event.isCancelled = true
}
private val bedTypes = EnumSet.copyOf(getMaterialsWithWoolColorPrefix("BED").toList())
@@ -185,7 +188,7 @@ class ParcelListeners(
val clickedBlock = event.clickedBlock
val parcel = clickedBlock?.let { world.getParcelAt(it) }
if (!user.hasPermBuildAnywhere && parcel.isPresentAnd { isBanned(user.privilegeKey) }) {
if (!user.hasPermBuildAnywhere && parcel != null && !parcel.canEnter(user)) {
user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
event.isCancelled = true; return@l
}
@@ -193,7 +196,8 @@ class ParcelListeners(
when (event.action) {
Action.RIGHT_CLICK_BLOCK -> run {
val type = clickedBlock.type
val interactable = parcel.effectiveInteractableConfig.isInteractable(type) || parcel.isPresentAnd { canBuild(user) }
val interactable = Interactables.listedMaterials.containsKey(type)
&& (parcel.effectiveInteractableConfig.isInteractable(type) || (parcel != null && parcel.canBuild(user)))
if (!interactable) {
val interactableClassName = Interactables[type]!!.name
user.sendParcelMessage(nopermit = true, message = "You cannot interact with $interactableClassName in this parcel")
@@ -206,19 +210,24 @@ class ParcelListeners(
val head = if (bed == Bed.Part.FOOT) clickedBlock.getRelative(bed.facing) else clickedBlock
when (head.biome) {
Biome.NETHER, Biome.THE_END -> {
if (world.options.disableExplosions || parcel.isNullOr { !canBuild(user) }) {
if (world.options.disableExplosions) {
user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
event.isCancelled = true; return@l
}
}
}
if (!canBuildOnArea(user, parcel)) {
user.sendParcelMessage(nopermit = true, message = "You may not sleep here")
event.isCancelled = true; return@l
}
}
onPlayerInteractEvent_RightClick(event, world, parcel)
}
Action.RIGHT_CLICK_AIR -> onPlayerInteractEvent_RightClick(event, world, parcel)
Action.PHYSICAL -> if (!user.hasPermBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || interactableConfig("pressure_plates") }) {
Action.PHYSICAL -> if (!canBuildOnArea(user, parcel) && !(parcel != null && parcel.interactableConfig("pressure_plates"))) {
user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
event.isCancelled = true; return@l
}
@@ -234,7 +243,7 @@ class ParcelListeners(
event.isCancelled = true; return
}
if (!parcel.canBuildN(event.player)) {
if (!canBuildOnArea(event.player, parcel)) {
when (item) {
LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> event.isCancelled = true
}
@@ -249,8 +258,8 @@ class ParcelListeners(
@Suppress("NON_EXHAUSTIVE_WHEN")
@field:ListenerMarker(priority = NORMAL)
val onPlayerInteractEntityEvent = RegistratorListener<PlayerInteractEntityEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.rightClicked.location.block) ?: return@l
if (ppa.canBuildN(event.player)) return@l
val (_, area) = getWorldAndArea(event.rightClicked.location.block) ?: return@l
if (canBuildOnArea(event.player, area)) return@l
when (event.rightClicked.type) {
EntityType.BOAT,
EntityType.MINECART,
@@ -281,14 +290,14 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onEntityChangeBlockEvent = RegistratorListener<EntityChangeBlockEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
if (event.entity.type == EntityType.ENDERMAN || ppa.isNullOr { hasBlockVisitors }) {
val (_, area) = getWorldAndArea(event.block) ?: return@l
if (event.entity.type == EntityType.ENDERMAN || area == null || area.hasBlockVisitors) {
event.isCancelled = true; return@l
}
if (event.entity.type == EntityType.FALLING_BLOCK) {
// a sand block started falling. Track it and delete it if it gets out of this parcel.
entityTracker.track(event.entity, ppa)
entityTracker.track(event.entity, area)
}
}
@@ -306,8 +315,8 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onPlayerDropItemEvent = RegistratorListener<PlayerDropItemEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.itemDrop.location.block) ?: return@l
if (!ppa.canBuildN(event.player) && !ppa.isPresentAnd { interactableConfig("containers") }) event.isCancelled = true
val (_, area) = getWorldAndArea(event.itemDrop.location.block) ?: return@l
if (!canInteract(event.player, area, "containers")) event.isCancelled = true
}
/*
@@ -316,8 +325,8 @@ class ParcelListeners(
@field:ListenerMarker(priority = NORMAL)
val onEntityPickupItemEvent = RegistratorListener<EntityPickupItemEvent> l@{ event ->
val user = event.entity as? Player ?: return@l
val (wo, ppa) = getWoAndPPa(event.item.location.block) ?: return@l
if (!ppa.canBuildN(user)) event.isCancelled = true
val (_, area) = getWorldAndArea(event.item.location.block) ?: return@l
if (!canInteract(user, area, "containers")) event.isCancelled = true
}
/*
@@ -327,8 +336,8 @@ class ParcelListeners(
val onInventoryClickEvent = RegistratorListener<InventoryInteractEvent> l@{ event ->
val user = event.whoClicked as? Player ?: return@l
if ((event.inventory ?: return@l).holder === user) return@l // inventory null: hotbar
val (wo, ppa) = getWoAndPPa(event.inventory.location.block) ?: return@l
if (ppa.isNullOr { !canBuild(user) && !interactableConfig("containers") }) {
val (_, area) = getWorldAndArea(event.inventory.location.block) ?: return@l
if (!canInteract(user, area, "containers")) {
event.isCancelled = true
}
}
@@ -358,10 +367,10 @@ class ParcelListeners(
@ListenerMarker(priority = NORMAL)
val onBlockFormEvent = RegistratorListener<BlockFormEvent> l@{ event ->
val block = event.block
val (wo, ppa) = getWoAndPPa(block) ?: return@l
val (world, area) = getWorldAndArea(block) ?: return@l
// prevent any generation whatsoever on paths
if (ppa == null) {
if (area == null) {
event.isCancelled = true; return@l
}
@@ -371,10 +380,10 @@ class ParcelListeners(
val cancel: Boolean = when (event.newState.type) {
// prevent ice generation from Frost Walkers enchantment
FROSTED_ICE -> player != null && !ppa.canBuild(player)
FROSTED_ICE -> player != null && !area.canBuild(player)
// prevent snow generation from weather
SNOW -> !hasEntity && wo.options.preventWeatherBlockChanges
SNOW -> !hasEntity && world.options.preventWeatherBlockChanges
else -> false
}
@@ -392,7 +401,7 @@ class ParcelListeners(
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
if (event.entity is Creature && world.options.blockMobSpawning) {
event.isCancelled = true
} else if (world.getParcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
} else if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
event.isCancelled = true
}
}
@@ -402,8 +411,8 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onVehicleMoveEvent = RegistratorListener<VehicleMoveEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.to.block) ?: return@l
if (ppa == null) {
val (_, area) = getWorldAndArea(event.to.block) ?: return@l
if (area == null) {
event.vehicle.passengers.forEach {
if (it.type == EntityType.PLAYER) {
(it as Player).sendParcelMessage(except = true, message = "Your ride ends here")
@@ -411,7 +420,7 @@ class ParcelListeners(
}
event.vehicle.eject()
event.vehicle.remove()
} else if (ppa.hasBlockVisitors) {
} else if (area.hasBlockVisitors) {
event.to.subtract(event.to).add(event.from)
}
}
@@ -432,7 +441,7 @@ class ParcelListeners(
?: (event.damager as? Projectile)?.let { it.shooter as? Player }
?: return@l
if (!world.getParcelAt(event.entity).canBuildN(user)) {
if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
event.isCancelled = true
}
}
@@ -444,7 +453,7 @@ class ParcelListeners(
event.isCancelled = true; return@l
}
if (world.getParcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
event.isCancelled = true
}
}
@@ -457,7 +466,7 @@ class ParcelListeners(
val onHangingBreakByEntityEvent = RegistratorListener<HangingBreakByEntityEvent> l@{ event ->
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
val user = event.remover as? Player ?: return@l
if (!world.getParcelAt(event.entity).canBuildN(user)) {
if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
event.isCancelled = true
}
}
@@ -469,7 +478,7 @@ class ParcelListeners(
val onHangingPlaceEvent = RegistratorListener<HangingPlaceEvent> l@{ event ->
val world = parcelProvider.getWorld(event.entity.world) ?: return@l
val block = event.block.getRelative(event.blockFace)
if (!world.getParcelAt(block).canBuildN(event.player)) {
if (!canBuildOnArea(event.player, world.getParcelAt(block))) {
event.isCancelled = true
}
}
@@ -479,16 +488,16 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onStructureGrowEvent = RegistratorListener<StructureGrowEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.location.block) ?: return@l
if (ppa == null) {
val (world, area) = getWorldAndArea(event.location.block) ?: return@l
if (area == null) {
event.isCancelled = true; return@l
}
if (!event.player.hasPermBuildAnywhere && !ppa.canBuild(event.player)) {
if (!event.player.hasPermBuildAnywhere && !area.canBuild(event.player)) {
event.isCancelled = true; return@l
}
event.blocks.removeIf { wo.getParcelAt(it.block) !== ppa }
event.blocks.removeIf { world.getParcelAt(it.block) !== area }
}
/*
@@ -511,9 +520,9 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onItemSpawnEvent = RegistratorListener<ItemSpawnEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.location.block) ?: return@l
if (ppa == null) event.isCancelled = true
else entityTracker.track(event.entity, ppa)
val (_, area) = getWorldAndArea(event.location.block) ?: return@l
if (area == null) event.isCancelled = true
else entityTracker.track(event.entity, area)
}
/*
@@ -521,8 +530,8 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onEntityTeleportEvent = RegistratorListener<EntityTeleportEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.from.block) ?: return@l
if (ppa !== wo.getParcelAt(event.to)) {
val (world, area) = getWorldAndArea(event.from.block) ?: return@l
if (area !== world.getParcelAt(event.to)) {
event.isCancelled = true
}
}
@@ -533,11 +542,11 @@ class ParcelListeners(
*/
@field:ListenerMarker(priority = NORMAL)
val onProjectileLaunchEvent = RegistratorListener<ProjectileLaunchEvent> l@{ event ->
val (wo, ppa) = getWoAndPPa(event.entity.location.block) ?: return@l
if (ppa == null || (event.entity.shooter as? Player)?.let { !ppa.canBuildN(it) } == true) {
val (_, area) = getWorldAndArea(event.entity.location.block) ?: return@l
if (area == null || (event.entity.shooter as? Player)?.let { !canBuildOnArea(it, area) } == true) {
event.isCancelled = true
} else {
entityTracker.track(event.entity, ppa)
entityTracker.track(event.entity, area)
}
}

View File

@@ -43,7 +43,7 @@ class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) {
private fun canBuild(x: Int, z: Int): Boolean {
world.getParcelAt(x, z)?.let { parcel ->
if (parcel.canBuild(player, checkAdmin = false)) {
if (parcel.canBuildFast(player)) {
return true
}
}

View File

@@ -7,8 +7,8 @@ import io.dico.parcels2.Privilege.DEFAULT
import kotlinx.coroutines.channels.SendChannel
import org.jetbrains.exposed.sql.*
object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_added_local", ParcelsT)
object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_added_global", ProfilesT)
object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
object ParcelOptionsT : Table("parcel_options") {
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)

View File

@@ -20,8 +20,8 @@ inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) blo
inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
if (this == null) block()
return this

View File

@@ -13,10 +13,14 @@ inline val OfflinePlayer.uuid get() = uniqueId
inline val OfflinePlayer.isValid
get() = isOnline() || hasPlayedBefore()
inline val Player.hasPermBanBypass get() = hasPermission("parcels.admin.bypass.ban")
const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
inline val Player.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
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.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
inline val Player.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
inline val Player.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
inline val Player.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
val Player.parcelLimit: Int