Archived
0

Do some work on interactables

This commit is contained in:
Dico
2018-09-23 20:42:14 +01:00
parent 535df42c54
commit b0d1fab486
18 changed files with 232 additions and 171 deletions

View File

@@ -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
}

View File

@@ -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,
private constructor(
val id: Int,
val name: String,
val interactableByDefault: Boolean,
vararg val materials: Material) {
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

View File

@@ -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()
}

View File

@@ -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,

View File

@@ -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)
}
}

View File

@@ -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,
class ParcelImpl(
override val world: ParcelWorld,
override val x: Int,
override val z: Int) : Parcel, ParcelId {
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,11 +85,13 @@ 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 {
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 {
override fun clear(): Boolean =
data.interactableConfig.clear().alsoIfTrue {
// TODO update storage
}
}
@@ -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(")")
}
}
}*/
}
}

View File

@@ -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,
class ParcelListeners(
val parcelProvider: ParcelProvider,
val entityTracker: ParcelEntityTracker,
val storage: Storage) {
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) {
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 -> run {
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
}
}

View File

@@ -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>>)

View 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)
}
}
}

View File

@@ -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) }

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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) }

View File

@@ -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),

View File

@@ -30,3 +30,6 @@ fun IntRange.clamp(min: Int, max: Int): IntRange {
}
return this
}
// the name coerceAtMost is bad
fun Int.clampMax(max: Int) = coerceAtMost(max)

View File

@@ -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