Do some work on interactables
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
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>
|
||||
@@ -11,20 +16,20 @@ fun MutableAddedDataMap(): MutableAddedDataMap = hashMapOf()
|
||||
|
||||
interface AddedData {
|
||||
val addedMap: AddedDataMap
|
||||
var addedStatusOfStar: AddedStatus
|
||||
var statusOfStar: AddedStatus
|
||||
|
||||
fun getAddedStatus(key: StatusKey): AddedStatus
|
||||
fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean
|
||||
fun getStatus(key: StatusKey): AddedStatus
|
||||
fun setStatus(key: StatusKey, status: AddedStatus): Boolean
|
||||
|
||||
fun compareAndSetAddedStatus(key: StatusKey, expect: AddedStatus, status: AddedStatus): Boolean =
|
||||
getAddedStatus(key) == expect && setAddedStatus(key, status)
|
||||
fun casStatus(key: StatusKey, expect: AddedStatus, status: AddedStatus): Boolean =
|
||||
getStatus(key) == expect && setStatus(key, status)
|
||||
|
||||
fun isAllowed(key: StatusKey) = getAddedStatus(key) == AddedStatus.ALLOWED
|
||||
fun allow(key: StatusKey) = setAddedStatus(key, AddedStatus.ALLOWED)
|
||||
fun disallow(key: StatusKey) = compareAndSetAddedStatus(key, AddedStatus.ALLOWED, AddedStatus.DEFAULT)
|
||||
fun isBanned(key: StatusKey) = getAddedStatus(key) == AddedStatus.BANNED
|
||||
fun ban(key: StatusKey) = setAddedStatus(key, AddedStatus.BANNED)
|
||||
fun unban(key: StatusKey) = compareAndSetAddedStatus(key, AddedStatus.BANNED, AddedStatus.DEFAULT)
|
||||
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)
|
||||
@@ -34,29 +39,20 @@ interface AddedData {
|
||||
fun unban(player: OfflinePlayer) = unban(player.statusKey)
|
||||
}
|
||||
|
||||
inline val OfflinePlayer.statusKey get() = PlayerProfile.nameless(this)
|
||||
inline val OfflinePlayer.statusKey: StatusKey
|
||||
get() = PlayerProfile.nameless(this)
|
||||
|
||||
open class AddedDataHolder(override var addedMap: MutableAddedDataMap = MutableAddedDataMap()) : AddedData {
|
||||
override var addedStatusOfStar: AddedStatus = AddedStatus.DEFAULT
|
||||
override var statusOfStar: AddedStatus = DEFAULT
|
||||
|
||||
override fun getAddedStatus(key: StatusKey): AddedStatus = addedMap.getOrDefault(key, addedStatusOfStar)
|
||||
override fun getStatus(key: StatusKey): AddedStatus = addedMap.getOrDefault(key, statusOfStar)
|
||||
|
||||
override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
return if (status.isDefault) addedMap.remove(key) != null
|
||||
override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
return if (status == DEFAULT) addedMap.remove(key) != null
|
||||
else addedMap.put(key, status) != status
|
||||
}
|
||||
}
|
||||
|
||||
enum class AddedStatus {
|
||||
DEFAULT,
|
||||
ALLOWED,
|
||||
BANNED;
|
||||
|
||||
inline val isDefault get() = this == DEFAULT
|
||||
inline val isAllowed get() = this == ALLOWED
|
||||
inline val isBanned get() = this == BANNED
|
||||
}
|
||||
|
||||
interface GlobalAddedData : AddedData {
|
||||
val owner: PlayerProfile
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package io.dico.parcels2
|
||||
|
||||
import io.dico.parcels2.util.ext.findWoodKindPrefixedMaterials
|
||||
import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
|
||||
import org.bukkit.Material
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.EnumMap
|
||||
|
||||
class Interactables
|
||||
private constructor(val id: Int,
|
||||
val name: String,
|
||||
val interactableByDefault: Boolean,
|
||||
vararg val materials: Material) {
|
||||
private constructor(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val interactableByDefault: Boolean,
|
||||
vararg val materials: Material
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val classesById: List<Interactables>
|
||||
@@ -22,35 +25,64 @@ private constructor(val id: Int,
|
||||
listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
|
||||
}
|
||||
|
||||
operator fun get(material: Material): Interactables? {
|
||||
val id = listedMaterials[material] ?: return null
|
||||
return classesById[id]
|
||||
}
|
||||
|
||||
operator fun get(name: String): Interactables {
|
||||
return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
|
||||
}
|
||||
|
||||
operator fun get(id: Int): Interactables {
|
||||
return classesById[id]
|
||||
}
|
||||
|
||||
private fun getClassesArray() = run {
|
||||
var id = 0
|
||||
@Suppress("UNUSED_CHANGED_VALUE")
|
||||
arrayOf(
|
||||
Interactables(id++, "button", true,
|
||||
Interactables(
|
||||
id++, "buttons", true,
|
||||
Material.STONE_BUTTON,
|
||||
*findWoodKindPrefixedMaterials("BUTTON")
|
||||
*getMaterialsWithWoodTypePrefix("BUTTON")
|
||||
),
|
||||
|
||||
Interactables(id++, "lever", true,
|
||||
Material.LEVER),
|
||||
Interactables(
|
||||
id++, "levers", true,
|
||||
Material.LEVER
|
||||
),
|
||||
|
||||
Interactables(id++, "pressure_plate", true,
|
||||
Interactables(
|
||||
id++, "pressure_plates", true,
|
||||
Material.STONE_PRESSURE_PLATE,
|
||||
*findWoodKindPrefixedMaterials("PRESSURE_PLATE"),
|
||||
*getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
|
||||
Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
|
||||
Material.LIGHT_WEIGHTED_PRESSURE_PLATE),
|
||||
Material.LIGHT_WEIGHTED_PRESSURE_PLATE
|
||||
),
|
||||
|
||||
Interactables(id++, "redstone_components", false,
|
||||
Interactables(
|
||||
id++, "redstone", false,
|
||||
Material.COMPARATOR,
|
||||
Material.REPEATER),
|
||||
Material.REPEATER
|
||||
),
|
||||
|
||||
Interactables(id++, "containers", false,
|
||||
Interactables(
|
||||
id++, "containers", false,
|
||||
Material.CHEST,
|
||||
Material.TRAPPED_CHEST,
|
||||
Material.DISPENSER,
|
||||
Material.DROPPER,
|
||||
Material.HOPPER,
|
||||
Material.FURNACE)
|
||||
Material.FURNACE
|
||||
),
|
||||
|
||||
Interactables(
|
||||
id++, "gates", true,
|
||||
*getMaterialsWithWoodTypePrefix("DOOR"),
|
||||
*getMaterialsWithWoodTypePrefix("TRAPDOOR"),
|
||||
*getMaterialsWithWoodTypePrefix("FENCE_GATE")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,6 +90,27 @@ private constructor(val id: Int,
|
||||
|
||||
}
|
||||
|
||||
val Parcel?.effectiveInteractableConfig: InteractableConfiguration
|
||||
get() = this?.interactableConfig ?: pathInteractableConfig
|
||||
|
||||
val pathInteractableConfig: InteractableConfiguration = run {
|
||||
val data = BitmaskInteractableConfiguration().apply {
|
||||
Interactables.classesById.forEach {
|
||||
setInteractable(it, false)
|
||||
}
|
||||
}
|
||||
object : InteractableConfiguration by data {
|
||||
override fun setInteractable(clazz: Interactables, interactable: Boolean) =
|
||||
throw IllegalStateException("pathInteractableConfig is immutable")
|
||||
|
||||
override fun clear() =
|
||||
throw IllegalStateException("pathInteractableConfig is immutable")
|
||||
|
||||
override fun copyFrom(other: InteractableConfiguration) =
|
||||
throw IllegalStateException("pathInteractableConfig is immutable")
|
||||
}
|
||||
}
|
||||
|
||||
interface InteractableConfiguration {
|
||||
val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
|
||||
fun isInteractable(material: Material): Boolean
|
||||
@@ -67,8 +120,13 @@ interface InteractableConfiguration {
|
||||
fun copyFrom(other: InteractableConfiguration) {
|
||||
Interactables.classesById.forEach { setInteractable(it, other.isInteractable(it)) }
|
||||
}
|
||||
|
||||
operator fun invoke(material: Material) = isInteractable(material)
|
||||
operator fun invoke(className: String) = isInteractable(Interactables[className])
|
||||
}
|
||||
|
||||
fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
|
||||
|
||||
class BitmaskInteractableConfiguration : InteractableConfiguration {
|
||||
val bitmaskArray = IntArray((Interactables.classesById.size + 31) / 32)
|
||||
|
||||
@@ -98,7 +156,7 @@ class BitmaskInteractableConfiguration : InteractableConfiguration {
|
||||
override fun clear(): Boolean {
|
||||
var change = false
|
||||
for (i in bitmaskArray.indices) {
|
||||
change = change || bitmaskArray[i] != 0
|
||||
if (!change && bitmaskArray[i] != 0) change = true
|
||||
bitmaskArray[i] = 0
|
||||
}
|
||||
return change
|
||||
|
||||
@@ -45,9 +45,6 @@ interface ParcelData : AddedData {
|
||||
|
||||
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
|
||||
|
||||
var allowInteractInputs: Boolean
|
||||
var allowInteractInventory: Boolean
|
||||
|
||||
fun isOwner(uuid: UUID): Boolean {
|
||||
return owner?.uuid == uuid
|
||||
}
|
||||
@@ -66,8 +63,6 @@ class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf())
|
||||
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|
||||
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
|
||||
|
||||
override var allowInteractInputs = true
|
||||
override var allowInteractInventory = true
|
||||
override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ 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,",
|
||||
@@ -28,7 +29,7 @@ class CommandsParcelOptions(plugin: ParcelsPlugin) : AbstractParcelCommands(plug
|
||||
@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,
|
||||
|
||||
@@ -20,12 +20,12 @@ class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataMan
|
||||
private inline var data get() = addedMap; set(value) = run { addedMap = value }
|
||||
private inline val isEmpty get() = data === emptyData
|
||||
|
||||
override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
if (isEmpty) {
|
||||
if (status == AddedStatus.DEFAULT) return false
|
||||
data = mutableMapOf()
|
||||
}
|
||||
return super.setAddedStatus(key, status).alsoIfTrue {
|
||||
return super.setStatus(key, status).alsoIfTrue {
|
||||
plugin.storage.setGlobalAddedStatus(owner, key, status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import org.bukkit.OfflinePlayer
|
||||
import org.joda.time.DateTime
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ParcelImpl(override val world: ParcelWorld,
|
||||
override val x: Int,
|
||||
override val z: Int) : Parcel, ParcelId {
|
||||
class ParcelImpl(
|
||||
override val world: ParcelWorld,
|
||||
override val x: Int,
|
||||
override val z: Int
|
||||
) : Parcel, ParcelId {
|
||||
override val id: ParcelId = this
|
||||
override val pos get() = Vec2i(x, z)
|
||||
override var data: ParcelDataHolder = ParcelDataHolder(); private set
|
||||
@@ -34,7 +36,7 @@ class ParcelImpl(override val world: ParcelWorld,
|
||||
}
|
||||
|
||||
override val addedMap: AddedDataMap get() = data.addedMap
|
||||
override fun getAddedStatus(key: StatusKey) = data.getAddedStatus(key)
|
||||
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 fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean): Boolean {
|
||||
@@ -42,9 +44,9 @@ class ParcelImpl(override val world: ParcelWorld,
|
||||
|| checkGlobal && world.globalAddedData[owner ?: return false].isAllowed(player)
|
||||
}
|
||||
|
||||
override var addedStatusOfStar: AddedStatus
|
||||
get() = data.addedStatusOfStar
|
||||
set(value) = run { setAddedStatus(PlayerProfile.Star, value) }
|
||||
override var statusOfStar: AddedStatus
|
||||
get() = data.statusOfStar
|
||||
set(value) = run { setStatus(PlayerProfile.Star, value) }
|
||||
|
||||
val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap }
|
||||
|
||||
@@ -69,28 +71,12 @@ class ParcelImpl(override val world: ParcelWorld,
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
return data.setAddedStatus(key, status).alsoIfTrue {
|
||||
override fun setStatus(key: StatusKey, status: AddedStatus): Boolean {
|
||||
return data.setStatus(key, status).alsoIfTrue {
|
||||
world.storage.setParcelPlayerStatus(this, key, status)
|
||||
}
|
||||
}
|
||||
|
||||
override var allowInteractInputs: Boolean
|
||||
get() = data.allowInteractInputs
|
||||
set(value) {
|
||||
if (data.allowInteractInputs == value) return
|
||||
world.storage.setParcelAllowsInteractInputs(this, value)
|
||||
data.allowInteractInputs = value
|
||||
}
|
||||
|
||||
override var allowInteractInventory: Boolean
|
||||
get() = data.allowInteractInventory
|
||||
set(value) {
|
||||
if (data.allowInteractInventory == value) return
|
||||
world.storage.setParcelAllowsInteractInventory(this, value)
|
||||
data.allowInteractInventory = value
|
||||
}
|
||||
|
||||
private var _interactableConfig: InteractableConfiguration? = null
|
||||
override var interactableConfig: InteractableConfiguration
|
||||
get() {
|
||||
@@ -99,13 +85,15 @@ class ParcelImpl(override val world: ParcelWorld,
|
||||
override fun isInteractable(material: Material): Boolean = data.interactableConfig.isInteractable(material)
|
||||
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
|
||||
}
|
||||
override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean =
|
||||
data.interactableConfig.setInteractable(clazz, interactable).alsoIfTrue {
|
||||
// TODO update storage
|
||||
}
|
||||
|
||||
override fun clear(): Boolean = data.interactableConfig.clear().alsoIfTrue {
|
||||
// TODO update storage
|
||||
}
|
||||
override fun clear(): Boolean =
|
||||
data.interactableConfig.clear().alsoIfTrue {
|
||||
// TODO update storage
|
||||
}
|
||||
}
|
||||
}
|
||||
return _interactableConfig!!
|
||||
@@ -165,9 +153,11 @@ private object ParcelInfoStringComputer {
|
||||
append(infoStringColor1)
|
||||
append(')')
|
||||
}) {
|
||||
stringList.joinTo(this,
|
||||
stringList.joinTo(
|
||||
this,
|
||||
separator = infoStringColor1.toString() + ", " + infoStringColor2,
|
||||
limit = 150)
|
||||
limit = 150
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +188,7 @@ private object ParcelInfoStringComputer {
|
||||
append('\n')
|
||||
appendAddedList(local, global, AddedStatus.BANNED, "Banned")
|
||||
|
||||
/* TODO options
|
||||
if (!parcel.allowInteractInputs || !parcel.allowInteractInventory) {
|
||||
appendField("Options") {
|
||||
append("(")
|
||||
@@ -206,7 +197,7 @@ private object ParcelInfoStringComputer {
|
||||
appendField("inventory") { append(parcel.allowInteractInventory) }
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,7 @@ package io.dico.parcels2.listener
|
||||
import gnu.trove.TLongCollection
|
||||
import io.dico.dicore.ListenerMarker
|
||||
import io.dico.dicore.RegistratorListener
|
||||
import io.dico.parcels2.Parcel
|
||||
import io.dico.parcels2.ParcelProvider
|
||||
import io.dico.parcels2.ParcelWorld
|
||||
import io.dico.parcels2.statusKey
|
||||
import io.dico.parcels2.*
|
||||
import io.dico.parcels2.storage.Storage
|
||||
import io.dico.parcels2.util.ext.*
|
||||
import org.bukkit.Material.*
|
||||
@@ -31,11 +28,14 @@ import org.bukkit.event.weather.WeatherChangeEvent
|
||||
import org.bukkit.event.world.ChunkLoadEvent
|
||||
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) {
|
||||
class ParcelListeners(
|
||||
val parcelProvider: ParcelProvider,
|
||||
val entityTracker: ParcelEntityTracker,
|
||||
val storage: Storage
|
||||
) {
|
||||
private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasBuildAnywhere
|
||||
|
||||
/**
|
||||
@@ -170,6 +170,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider,
|
||||
if (ppa.isNullOr { hasBlockVisitors }) event.isCancelled = true
|
||||
}
|
||||
|
||||
private val bedTypes = EnumSet.copyOf(getMaterialsWithWoodTypePrefix("BED").toList())
|
||||
/*
|
||||
* Prevents players from placing liquids, using flint and steel, changing redstone components,
|
||||
* using inputs (unless allowed by the plot),
|
||||
@@ -191,49 +192,33 @@ class ParcelListeners(val parcelProvider: ParcelProvider,
|
||||
|
||||
when (event.action) {
|
||||
Action.RIGHT_CLICK_BLOCK -> run {
|
||||
when (clickedBlock.type) {
|
||||
REPEATER,
|
||||
COMPARATOR -> run {
|
||||
if (!parcel.canBuildN(user)) {
|
||||
event.isCancelled = true; return@l
|
||||
}
|
||||
}
|
||||
LEVER,
|
||||
STONE_BUTTON,
|
||||
ANVIL,
|
||||
TRAPPED_CHEST,
|
||||
OAK_BUTTON, BIRCH_BUTTON, SPRUCE_BUTTON, JUNGLE_BUTTON, ACACIA_BUTTON, DARK_OAK_BUTTON,
|
||||
OAK_FENCE_GATE, BIRCH_FENCE_GATE, SPRUCE_FENCE_GATE, JUNGLE_FENCE_GATE, ACACIA_FENCE_GATE, DARK_OAK_FENCE_GATE,
|
||||
OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR,
|
||||
OAK_TRAPDOOR, BIRCH_TRAPDOOR, SPRUCE_TRAPDOOR, JUNGLE_TRAPDOOR, ACACIA_TRAPDOOR, DARK_OAK_TRAPDOOR
|
||||
-> run {
|
||||
if (!user.hasBuildAnywhere && !parcel.isNullOr { canBuild(user) || allowInteractInputs }) {
|
||||
user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
|
||||
event.isCancelled = true; return@l
|
||||
}
|
||||
}
|
||||
val type = clickedBlock.type
|
||||
val interactable = parcel.effectiveInteractableConfig.isInteractable(type) || parcel.isPresentAnd { canBuild(user) }
|
||||
if (!interactable) {
|
||||
val interactableClassName = Interactables[type]!!.name
|
||||
user.sendParcelMessage(nopermit = true, message = "You cannot interact with $interactableClassName in this parcel")
|
||||
event.isCancelled = true
|
||||
return@l
|
||||
}
|
||||
|
||||
WHITE_BED, ORANGE_BED, MAGENTA_BED, LIGHT_BLUE_BED, YELLOW_BED, LIME_BED, PINK_BED, GRAY_BED, LIGHT_GRAY_BED, CYAN_BED, PURPLE_BED, BLUE_BED, BROWN_BED, GREEN_BED, RED_BED, BLACK_BED
|
||||
-> run {
|
||||
if (world.options.disableExplosions) {
|
||||
val bed = clickedBlock.blockData as Bed
|
||||
val head = if (bed == Bed.Part.FOOT) clickedBlock.getRelative(bed.facing) else clickedBlock
|
||||
when (head.biome) {
|
||||
Biome.NETHER, Biome.THE_END -> run {
|
||||
user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
|
||||
event.isCancelled = true; return@l
|
||||
}
|
||||
if (bedTypes.contains(type)) {
|
||||
val bed = clickedBlock.blockData as Bed
|
||||
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) }) {
|
||||
user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
|
||||
event.isCancelled = true; return@l
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerInteractEvent_RightClick(event, world, parcel)
|
||||
}
|
||||
|
||||
Action.RIGHT_CLICK_AIR -> onPlayerInteractEvent_RightClick(event, world, parcel)
|
||||
Action.PHYSICAL -> if (!user.hasBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || allowInteractInputs }) {
|
||||
Action.PHYSICAL -> if (!user.hasBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || interactableConfig("pressure_plates") }) {
|
||||
user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
|
||||
event.isCancelled = true; return@l
|
||||
}
|
||||
@@ -322,7 +307,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider,
|
||||
@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 { allowInteractInventory }) event.isCancelled = true
|
||||
if (!ppa.canBuildN(event.player) && !ppa.isPresentAnd { interactableConfig("containers") }) event.isCancelled = true
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,7 +328,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider,
|
||||
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) && !allowInteractInventory }) {
|
||||
if (ppa.isNullOr { !canBuild(user) && !interactableConfig("containers") }) {
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
@@ -385,10 +370,10 @@ class ParcelListeners(val parcelProvider: ParcelProvider,
|
||||
|
||||
val cancel: Boolean = when (event.newState.type) {
|
||||
|
||||
// prevent ice generation from Frost Walkers enchantment
|
||||
// prevent ice generation from Frost Walkers enchantment
|
||||
FROSTED_ICE -> player != null && !ppa.canBuild(player)
|
||||
|
||||
// prevent snow generation from weather
|
||||
// prevent snow generation from weather
|
||||
SNOW -> !hasEntity && wo.options.preventWeatherBlockChanges
|
||||
|
||||
else -> false
|
||||
|
||||
@@ -58,10 +58,12 @@ interface Backing {
|
||||
|
||||
fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
|
||||
|
||||
fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?)
|
||||
/*
|
||||
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)
|
||||
|
||||
fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean)
|
||||
|
||||
*/
|
||||
|
||||
fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>)
|
||||
|
||||
|
||||
38
src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
Normal file
38
src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.UUID
|
||||
|
||||
/* For putting it into the database */
|
||||
fun UUID.toByteArray(): ByteArray =
|
||||
ByteBuffer.allocate(16).apply {
|
||||
putLong(mostSignificantBits)
|
||||
putLong(leastSignificantBits)
|
||||
}.array()
|
||||
|
||||
/* For getting it out of the database */
|
||||
fun ByteArray.toUUID(): UUID =
|
||||
ByteBuffer.wrap(this).run {
|
||||
val mostSignificantBits = getLong()
|
||||
val leastSignificantBits = getLong()
|
||||
UUID(mostSignificantBits, leastSignificantBits)
|
||||
}
|
||||
|
||||
/* For putting it into the database */
|
||||
fun IntArray.toByteArray(): ByteArray =
|
||||
ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
|
||||
buf.asIntBuffer().put(this)
|
||||
}.array()
|
||||
|
||||
/* For getting it out of the database */
|
||||
fun ByteArray.toIntArray(): IntArray {
|
||||
if (this.size % Int.SIZE_BYTES != 0)
|
||||
throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
|
||||
|
||||
return ByteBuffer.wrap(this).run {
|
||||
IntArray(remaining() / 4).also { array ->
|
||||
asIntBuffer().get(array)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,9 +50,7 @@ interface Storage {
|
||||
|
||||
fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
|
||||
|
||||
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean): Job
|
||||
|
||||
fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean): Job
|
||||
fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray): Job
|
||||
|
||||
|
||||
fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>>
|
||||
@@ -100,9 +98,7 @@ class BackedStorage internal constructor(val b: Backing) : Storage {
|
||||
|
||||
override fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setLocalPlayerStatus(parcel, player, status) }
|
||||
|
||||
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInventory(parcel, value) }
|
||||
|
||||
override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInputs(parcel, value) }
|
||||
override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray) = b.launchJob { b.setParcelOptionsInteractBitmask(parcel, bitmask) }
|
||||
|
||||
|
||||
override fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalAddedData(it) }
|
||||
|
||||
@@ -5,12 +5,9 @@ package io.dico.parcels2.storage.exposed
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import io.dico.parcels2.*
|
||||
import io.dico.parcels2.PlayerProfile.Star.name
|
||||
import io.dico.parcels2.storage.AddedDataPair
|
||||
import io.dico.parcels2.storage.Backing
|
||||
import io.dico.parcels2.storage.DataPair
|
||||
import io.dico.parcels2.storage.*
|
||||
import io.dico.parcels2.util.ext.clampMax
|
||||
import io.dico.parcels2.util.ext.synchronized
|
||||
import io.dico.parcels2.util.toByteArray
|
||||
import io.dico.parcels2.util.toUUID
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.ArrayChannel
|
||||
import kotlinx.coroutines.channels.LinkedListChannel
|
||||
@@ -196,8 +193,9 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
||||
AddedLocalT.setPlayerStatus(parcel, profile, status)
|
||||
}
|
||||
|
||||
setParcelAllowsInteractInputs(parcel, data.allowInteractInputs)
|
||||
setParcelAllowsInteractInventory(parcel, data.allowInteractInventory)
|
||||
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)
|
||||
}
|
||||
|
||||
override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) {
|
||||
@@ -227,19 +225,19 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
||||
AddedLocalT.setPlayerStatus(parcel, player.toRealProfile(), status)
|
||||
}
|
||||
|
||||
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) {
|
||||
val id = ParcelsT.getOrInitId(parcel)
|
||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||
it[parcel_id] = id
|
||||
it[interact_inventory] = value
|
||||
override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?) {
|
||||
if (bitmask == null) {
|
||||
val id = ParcelsT.getId(parcel) ?: return
|
||||
ParcelOptionsT.deleteWhere { ParcelOptionsT.parcel_id eq id }
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) {
|
||||
if (bitmask.size != 1) throw IllegalArgumentException()
|
||||
val array = bitmask.toByteArray()
|
||||
val id = ParcelsT.getOrInitId(parcel)
|
||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||
it[parcel_id] = id
|
||||
it[interact_inputs] = value
|
||||
it[interact_bitmask] = array
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,8 +261,9 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
||||
|
||||
val id = row[ParcelsT.id]
|
||||
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
|
||||
allowInteractInputs = optrow[ParcelOptionsT.interact_inputs]
|
||||
allowInteractInventory = optrow[ParcelOptionsT.interact_inventory]
|
||||
val source = optrow[ParcelOptionsT.interact_bitmask].toIntArray()
|
||||
val target = (interactableConfig as? BitmaskInteractableConfiguration ?: return@let).bitmaskArray
|
||||
System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size))
|
||||
}
|
||||
|
||||
addedMap = AddedLocalT.readAddedData(id)
|
||||
|
||||
@@ -5,8 +5,8 @@ package io.dico.parcels2.storage.exposed
|
||||
import io.dico.parcels2.ParcelId
|
||||
import io.dico.parcels2.ParcelWorldId
|
||||
import io.dico.parcels2.PlayerProfile
|
||||
import io.dico.parcels2.util.toByteArray
|
||||
import io.dico.parcels2.util.toUUID
|
||||
import io.dico.parcels2.storage.toByteArray
|
||||
import io.dico.parcels2.storage.toUUID
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
package io.dico.parcels2.storage.exposed
|
||||
|
||||
import io.dico.parcels2.*
|
||||
import io.dico.parcels2.AddedStatus.ALLOWED
|
||||
import io.dico.parcels2.AddedStatus.DEFAULT
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import java.util.UUID
|
||||
@@ -12,8 +14,7 @@ object AddedGlobalT : AddedTable<PlayerProfile>("parcels_added_global", Profiles
|
||||
|
||||
object ParcelOptionsT : Table("parcel_options") {
|
||||
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
|
||||
val interact_inventory = bool("interact_inventory").default(true)
|
||||
val interact_inputs = bool("interact_inputs").default(true)
|
||||
val interact_bitmask = binary("interact_bitmask", 4).default(ByteArray(4) { 0 }) // all zero by default
|
||||
}
|
||||
|
||||
typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableAddedDataMap>>
|
||||
@@ -25,7 +26,7 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<
|
||||
val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
|
||||
|
||||
fun setPlayerStatus(attachedOn: AttachT, player: PlayerProfile.Real, status: AddedStatus) {
|
||||
if (status.isDefault) {
|
||||
if (status == DEFAULT) {
|
||||
val player_id = ProfilesT.getId(player) ?: return
|
||||
idTable.getId(attachedOn)?.let { holder ->
|
||||
deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
|
||||
@@ -38,7 +39,7 @@ 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.isAllowed
|
||||
it[allowed_flag] = status == ALLOWED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +98,6 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) AddedStatus.ALLOWED else AddedStatus.BANNED
|
||||
private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) ALLOWED else AddedStatus.BANNED
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import io.dico.parcels2.storage.Storage
|
||||
import io.dico.parcels2.storage.exposed.abs
|
||||
import io.dico.parcels2.storage.exposed.greater
|
||||
import io.dico.parcels2.storage.migration.Migration
|
||||
import io.dico.parcels2.util.toUUID
|
||||
import io.dico.parcels2.storage.toUUID
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.newFixedThreadPoolContext
|
||||
|
||||
@@ -11,10 +11,4 @@ fun getPlayerName(uuid: UUID): String? {
|
||||
return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name
|
||||
}
|
||||
|
||||
fun UUID.toByteArray(): ByteArray =
|
||||
ByteBuffer.allocate(16).apply {
|
||||
putLong(mostSignificantBits)
|
||||
putLong(leastSignificantBits)
|
||||
}.array()
|
||||
|
||||
fun ByteArray.toUUID(): UUID = ByteBuffer.wrap(this).run { UUID(long, long) }
|
||||
|
||||
@@ -79,7 +79,7 @@ private fun getMaterialPrefixed(prefix: String, name: String): Material {
|
||||
return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
|
||||
}
|
||||
|
||||
fun findWoodKindPrefixedMaterials(name: String) = arrayOf(
|
||||
fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
|
||||
getMaterialPrefixed("OAK", name),
|
||||
getMaterialPrefixed("BIRCH", name),
|
||||
getMaterialPrefixed("SPRUCE", name),
|
||||
@@ -88,7 +88,7 @@ fun findWoodKindPrefixedMaterials(name: String) = arrayOf(
|
||||
getMaterialPrefixed("DARK_OAK", name)
|
||||
)
|
||||
|
||||
fun findColorPrefixedMaterials(name: String) = arrayOf(
|
||||
fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
|
||||
getMaterialPrefixed("WHITE", name),
|
||||
getMaterialPrefixed("ORANGE", name),
|
||||
getMaterialPrefixed("MAGENTA", name),
|
||||
|
||||
@@ -29,4 +29,7 @@ fun IntRange.clamp(min: Int, max: Int): IntRange {
|
||||
return IntRange(first, max)
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
// the name coerceAtMost is bad
|
||||
fun Int.clampMax(max: Int) = coerceAtMost(max)
|
||||
2
todo.md
2
todo.md
@@ -79,4 +79,6 @@ Implement a container that doesn't require loading all parcel data on startup (C
|
||||
|
||||
~~Update player profiles in the database on join to account for name changes.~~
|
||||
|
||||
Store player status on parcel (allowed, default banned) as a number to allow for future additions to this set of possibilities
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user