Make progress with listeners
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ out/
|
|||||||
build/
|
build/
|
||||||
/debug/
|
/debug/
|
||||||
target/
|
target/
|
||||||
/gradle-output.txt
|
/gradle-output.txt
|
||||||
|
/*.java
|
||||||
@@ -118,7 +118,7 @@ tasks {
|
|||||||
"logback-core",
|
"logback-core",
|
||||||
"logback-classic",
|
"logback-classic",
|
||||||
|
|
||||||
"h2",
|
//"h2",
|
||||||
"HikariCP",
|
"HikariCP",
|
||||||
"kotlinx-coroutines-core",
|
"kotlinx-coroutines-core",
|
||||||
"kotlinx-coroutines-core-common",
|
"kotlinx-coroutines-core-common",
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import io.dico.dicore.Registrator
|
|||||||
import io.dico.dicore.command.EOverridePolicy
|
import io.dico.dicore.command.EOverridePolicy
|
||||||
import io.dico.dicore.command.ICommandDispatcher
|
import io.dico.dicore.command.ICommandDispatcher
|
||||||
import io.dico.parcels2.command.getParcelCommands
|
import io.dico.parcels2.command.getParcelCommands
|
||||||
import io.dico.parcels2.listener.ParcelEditListener
|
import io.dico.parcels2.listener.ParcelEntityTracker
|
||||||
|
import io.dico.parcels2.listener.ParcelListeners
|
||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.storage.yamlObjectMapper
|
import io.dico.parcels2.storage.yamlObjectMapper
|
||||||
import io.dico.parcels2.util.tryCreate
|
import io.dico.parcels2.util.tryCreate
|
||||||
@@ -19,20 +20,20 @@ private inline val plogger get() = logger
|
|||||||
const val debugging = true
|
const val debugging = true
|
||||||
|
|
||||||
class ParcelsPlugin : JavaPlugin() {
|
class ParcelsPlugin : JavaPlugin() {
|
||||||
lateinit var optionsFile: File
|
lateinit var optionsFile: File; private set
|
||||||
lateinit var options: Options
|
lateinit var options: Options; private set
|
||||||
lateinit var worlds: Worlds
|
lateinit var worlds: Worlds; private set
|
||||||
lateinit var storage: Storage
|
lateinit var storage: Storage; private set
|
||||||
|
|
||||||
|
val registrator = Registrator(this)
|
||||||
|
lateinit var entityTracker: ParcelEntityTracker; private set
|
||||||
|
private var listeners: ParcelListeners? = null
|
||||||
private var cmdDispatcher: ICommandDispatcher? = null
|
private var cmdDispatcher: ICommandDispatcher? = null
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
if (!init()) {
|
if (!init()) {
|
||||||
Bukkit.getPluginManager().disablePlugin(this)
|
Bukkit.getPluginManager().disablePlugin(this)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCommands()
|
|
||||||
registerListeners()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
@@ -61,6 +62,10 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entityTracker = ParcelEntityTracker(worlds)
|
||||||
|
registerListeners()
|
||||||
|
registerCommands()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +94,10 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun registerListeners() {
|
private fun registerListeners() {
|
||||||
Registrator(this).registerListeners(ParcelEditListener(worlds))
|
if (listeners != null) {
|
||||||
|
listeners = ParcelListeners(worlds, entityTracker)
|
||||||
|
registrator.registerListeners(listeners!!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
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.ParcelWorld
|
|
||||||
import io.dico.parcels2.Worlds
|
|
||||||
import io.dico.parcels2.util.hasBanBypass
|
|
||||||
import io.dico.parcels2.util.hasBuildAnywhere
|
|
||||||
import io.dico.parcels2.util.sendParcelMessage
|
|
||||||
import io.dico.parcels2.util.uuid
|
|
||||||
import org.bukkit.Material.*
|
|
||||||
import org.bukkit.block.Biome
|
|
||||||
import org.bukkit.block.Block
|
|
||||||
import org.bukkit.block.data.type.Bed
|
|
||||||
import org.bukkit.entity.Player
|
|
||||||
import org.bukkit.event.EventPriority.NORMAL
|
|
||||||
import org.bukkit.event.block.*
|
|
||||||
import org.bukkit.event.entity.EntityExplodeEvent
|
|
||||||
import org.bukkit.event.entity.ExplosionPrimeEvent
|
|
||||||
import org.bukkit.event.player.PlayerInteractEvent
|
|
||||||
import org.bukkit.event.player.PlayerMoveEvent
|
|
||||||
import org.bukkit.inventory.InventoryHolder
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
class ParcelEditListener(val worlds: Worlds) {
|
|
||||||
val entityTracker = ParcelEntityTracker()
|
|
||||||
|
|
||||||
private inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
|
|
||||||
private inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
|
|
||||||
private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasBuildAnywhere
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
private fun getWoAndPPa(block: Block): Pair<ParcelWorld, Parcel?>? {
|
|
||||||
val world = worlds.getWorld(block.world) ?: return null
|
|
||||||
return world to world.parcelAt(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents players from entering plots they are banned from
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onPlayerMove = RegistratorListener<PlayerMoveEvent> l@{ event ->
|
|
||||||
val user = event.player
|
|
||||||
if (user.hasBanBypass) return@l
|
|
||||||
val parcel = worlds.getParcelAt(event.to) ?: return@l
|
|
||||||
if (parcel.isBanned(user.uuid)) {
|
|
||||||
worlds.getParcelAt(event.from)?.also {
|
|
||||||
user.teleport(it.homeLocation)
|
|
||||||
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
|
|
||||||
} ?: run { event.to = event.from }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents players from breaking blocks outside of their parcels
|
|
||||||
* Prevents containers from dropping their contents when broken, if configured
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onBlockBreak = RegistratorListener<BlockBreakEvent> l@{ event ->
|
|
||||||
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
|
|
||||||
if (!event.player.hasBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
|
|
||||||
event.isCancelled = true; return@l
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wo.options.dropEntityItems) {
|
|
||||||
val state = event.block.state
|
|
||||||
if (state is InventoryHolder) {
|
|
||||||
state.inventory.clear()
|
|
||||||
state.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents players from placing blocks outside of their parcels
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onBlockPlace = RegistratorListener<BlockBreakEvent> l@{ event ->
|
|
||||||
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
|
|
||||||
if (!event.player.hasBuildAnywhere && !ppa.isNullOr { !canBuild(event.player) }) {
|
|
||||||
event.isCancelled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Control pistons
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onBlockPistonExtend = RegistratorListener<BlockPistonExtendEvent> l@{ event ->
|
|
||||||
checkPistonMovement(event, event.blocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onBlockPistonRetractEvent = RegistratorListener<BlockPistonRetractEvent> l@{ event ->
|
|
||||||
checkPistonMovement(event, event.blocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing some unnecessary optimizations here..
|
|
||||||
//@formatter:off
|
|
||||||
private inline fun Column(x: Int, z: Int): Long = x.toLong() or (z.toLong().shl(32))
|
|
||||||
|
|
||||||
private inline val Long.columnX get() = and(0xFFFF_FFFFL).toInt()
|
|
||||||
private inline val Long.columnZ get() = ushr(32).and(0xFFFF_FFFFL).toInt()
|
|
||||||
private inline fun TLongCollection.forEachInline(block: (Long) -> Unit) = iterator().let { while (it.hasNext()) block(it.next()) }
|
|
||||||
//@formatter:on
|
|
||||||
private fun checkPistonMovement(event: BlockPistonEvent, blocks: List<Block>) {
|
|
||||||
val world = worlds.getWorld(event.block.world) ?: return
|
|
||||||
val direction = event.direction
|
|
||||||
val columns = gnu.trove.set.hash.TLongHashSet(blocks.size * 2)
|
|
||||||
|
|
||||||
blocks.forEach {
|
|
||||||
columns.add(Column(it.x, it.z))
|
|
||||||
it.getRelative(direction).let { columns.add(Column(it.x, it.z)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
columns.forEachInline {
|
|
||||||
val ppa = world.parcelAt(it.columnX, it.columnZ)
|
|
||||||
if (ppa.isNullOr { hasBlockVisitors }) {
|
|
||||||
event.isCancelled = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents explosions if enabled by the configs for that world
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onExplosionPrimeEvent = RegistratorListener<ExplosionPrimeEvent> l@{ event ->
|
|
||||||
val (wo, ppa) = getWoAndPPa(event.entity.location.block) ?: return@l
|
|
||||||
if (ppa?.hasBlockVisitors == true) {
|
|
||||||
event.radius = 0F; event.isCancelled = true
|
|
||||||
} else if (wo.options.disableExplosions) {
|
|
||||||
event.radius = 0F
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents creepers and tnt minecarts from exploding if explosions are disabled
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onEntityExplodeEvent = RegistratorListener<EntityExplodeEvent> l@{ event ->
|
|
||||||
entityTracker.untrack(event.entity)
|
|
||||||
val world = worlds.getWorld(event.entity.world) ?: return@l
|
|
||||||
if (world.options.disableExplosions || world.parcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
|
|
||||||
event.isCancelled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents creepers and tnt minecarts from exploding if explosions are disabled
|
|
||||||
*/
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onBlockFromToEvent = RegistratorListener<BlockFromToEvent> l@{ event ->
|
|
||||||
val (wo, ppa) = getWoAndPPa(event.toBlock) ?: return@l
|
|
||||||
if (ppa.isNullOr { hasBlockVisitors }) event.isCancelled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevents players from placing liquids, using flint and steel, changing redstone components,
|
|
||||||
* using inputs (unless allowed by the plot),
|
|
||||||
* and using items disabled in the configuration for that world.
|
|
||||||
* Prevents player from using beds in HELL or SKY biomes if explosions are disabled.
|
|
||||||
*/
|
|
||||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
|
||||||
@ListenerMarker(priority = NORMAL)
|
|
||||||
val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event ->
|
|
||||||
val user = event.player
|
|
||||||
val world = worlds.getWorld(user.world) ?: return@l
|
|
||||||
val clickedBlock = event.clickedBlock
|
|
||||||
val parcel = clickedBlock?.let { world.parcelAt(it) }
|
|
||||||
|
|
||||||
if (!user.hasBuildAnywhere && parcel.isPresentAnd { isBanned(user.uuid) }) {
|
|
||||||
user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
|
|
||||||
event.isCancelled = true; return@l
|
|
||||||
}
|
|
||||||
|
|
||||||
when (event.action) {
|
|
||||||
Action.RIGHT_CLICK_BLOCK -> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action.RIGHT_CLICK_AIR -> if (event.hasItem()) {
|
|
||||||
val item = event.item.type
|
|
||||||
if (world.options.blockedItems.contains(item)) {
|
|
||||||
user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
|
|
||||||
event.isCancelled = true; return@l
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parcel.canBuildN(user)) {
|
|
||||||
when (item) {
|
|
||||||
LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> event.isCancelled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Action.PHYSICAL -> if (!user.hasBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || allowInteractInputs }) {
|
|
||||||
event.isCancelled = true; return@l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,53 @@
|
|||||||
package io.dico.parcels2.listener
|
package io.dico.parcels2.listener
|
||||||
|
|
||||||
import io.dico.parcels2.Parcel
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.Worlds
|
||||||
|
import io.dico.parcels2.util.editLoop
|
||||||
|
import io.dico.parcels2.util.isPresentAnd
|
||||||
import org.bukkit.entity.Entity
|
import org.bukkit.entity.Entity
|
||||||
|
|
||||||
class ParcelEntityTracker {
|
class ParcelEntityTracker(val worlds: Worlds) {
|
||||||
val map = mutableMapOf<Entity, Parcel>()
|
val map = mutableMapOf<Entity, Parcel?>()
|
||||||
|
|
||||||
fun untrack(entity: Entity) {
|
fun untrack(entity: Entity) {
|
||||||
map.remove(entity)
|
map.remove(entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun track(entity: Entity, parcel: Parcel?) {
|
||||||
|
map[entity] = parcel
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tracks entities. If the entity is dead, they are removed from the collection.
|
||||||
|
* If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
|
||||||
|
* If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
|
||||||
|
*
|
||||||
|
* Start after 5 seconds, run every 0.25 seconds
|
||||||
|
*/
|
||||||
|
fun tick() {
|
||||||
|
map.editLoop { entity, parcel ->
|
||||||
|
if (entity.isDead || entity.isOnGround) {
|
||||||
|
remove(); return@editLoop
|
||||||
|
}
|
||||||
|
if (parcel.isPresentAnd { hasBlockVisitors }) {
|
||||||
|
remove()
|
||||||
|
}
|
||||||
|
val newParcel = worlds.getParcelAt(entity.location)
|
||||||
|
if (newParcel !== parcel && !newParcel.isPresentAnd { hasBlockVisitors }) {
|
||||||
|
remove()
|
||||||
|
entity.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
|
||||||
|
map.editLoop {
|
||||||
|
if (value === parcel1) {
|
||||||
|
value = parcel2
|
||||||
|
} else if (value === parcel2) {
|
||||||
|
value = parcel1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
559
src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
Normal file
559
src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
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.ParcelWorld
|
||||||
|
import io.dico.parcels2.Worlds
|
||||||
|
import io.dico.parcels2.util.*
|
||||||
|
import org.bukkit.Material.*
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.block.Biome
|
||||||
|
import org.bukkit.block.Block
|
||||||
|
import org.bukkit.block.data.Directional
|
||||||
|
import org.bukkit.block.data.type.Bed
|
||||||
|
import org.bukkit.entity.*
|
||||||
|
import org.bukkit.entity.minecart.ExplosiveMinecart
|
||||||
|
import org.bukkit.event.EventPriority.NORMAL
|
||||||
|
import org.bukkit.event.block.*
|
||||||
|
import org.bukkit.event.entity.*
|
||||||
|
import org.bukkit.event.hanging.HangingBreakByEntityEvent
|
||||||
|
import org.bukkit.event.hanging.HangingBreakEvent
|
||||||
|
import org.bukkit.event.hanging.HangingPlaceEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryInteractEvent
|
||||||
|
import org.bukkit.event.player.*
|
||||||
|
import org.bukkit.event.vehicle.VehicleMoveEvent
|
||||||
|
import org.bukkit.event.weather.WeatherChangeEvent
|
||||||
|
import org.bukkit.event.world.StructureGrowEvent
|
||||||
|
import org.bukkit.event.world.WorldLoadEvent
|
||||||
|
import org.bukkit.inventory.InventoryHolder
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
class ParcelListeners(val worlds: Worlds, val entityTracker: ParcelEntityTracker) {
|
||||||
|
private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasBuildAnywhere
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
private fun getWoAndPPa(block: Block): Pair<ParcelWorld, Parcel?>? {
|
||||||
|
val world = worlds.getWorld(block.world) ?: return null
|
||||||
|
return world to world.parcelAt(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from entering plots they are banned from
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
|
||||||
|
val user = event.player
|
||||||
|
if (user.hasBanBypass) return@l
|
||||||
|
val parcel = worlds.getParcelAt(event.to) ?: return@l
|
||||||
|
if (parcel.isBanned(user.uuid)) {
|
||||||
|
worlds.getParcelAt(event.from)?.also {
|
||||||
|
user.teleport(it.homeLocation)
|
||||||
|
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
|
||||||
|
} ?: run { event.to = event.from }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from breaking blocks outside of their parcels
|
||||||
|
* Prevents containers from dropping their contents when broken, if configured
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onBlockBreakEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
|
||||||
|
if (!event.player.hasBuildAnywhere && ppa.isNullOr { !canBuild(event.player) }) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wo.options.dropEntityItems) {
|
||||||
|
val state = event.block.state
|
||||||
|
if (state is InventoryHolder) {
|
||||||
|
state.inventory.clear()
|
||||||
|
state.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from placing blocks outside of their parcels
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onBlockPlaceEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.block) ?: return@l
|
||||||
|
if (!event.player.hasBuildAnywhere && !ppa.isNullOr { !canBuild(event.player) }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Control pistons
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onBlockPistonExtendEvent = RegistratorListener<BlockPistonExtendEvent> l@{ event ->
|
||||||
|
checkPistonMovement(event, event.blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onBlockPistonRetractEvent = RegistratorListener<BlockPistonRetractEvent> l@{ event ->
|
||||||
|
checkPistonMovement(event, event.blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing some unnecessary optimizations here..
|
||||||
|
//@formatter:off
|
||||||
|
private inline fun Column(x: Int, z: Int): Long = x.toLong() or (z.toLong().shl(32))
|
||||||
|
|
||||||
|
private inline val Long.columnX get() = and(0xFFFF_FFFFL).toInt()
|
||||||
|
private inline val Long.columnZ get() = ushr(32).and(0xFFFF_FFFFL).toInt()
|
||||||
|
private inline fun TLongCollection.forEachInline(block: (Long) -> Unit) = iterator().let { while (it.hasNext()) block(it.next()) }
|
||||||
|
//@formatter:on
|
||||||
|
private fun checkPistonMovement(event: BlockPistonEvent, blocks: List<Block>) {
|
||||||
|
val world = worlds.getWorld(event.block.world) ?: return
|
||||||
|
val direction = event.direction
|
||||||
|
val columns = gnu.trove.set.hash.TLongHashSet(blocks.size * 2)
|
||||||
|
|
||||||
|
blocks.forEach {
|
||||||
|
columns.add(Column(it.x, it.z))
|
||||||
|
it.getRelative(direction).let { columns.add(Column(it.x, it.z)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.forEachInline {
|
||||||
|
val ppa = world.parcelAt(it.columnX, it.columnZ)
|
||||||
|
if (ppa.isNullOr { hasBlockVisitors }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents explosions if enabled by the configs for that world
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onExplosionPrimeEvent = RegistratorListener<ExplosionPrimeEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.entity.location.block) ?: return@l
|
||||||
|
if (ppa?.hasBlockVisitors == true) {
|
||||||
|
event.radius = 0F; event.isCancelled = true
|
||||||
|
} else if (wo.options.disableExplosions) {
|
||||||
|
event.radius = 0F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents creepers and tnt minecarts from exploding if explosions are disabled
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntityExplodeEvent = RegistratorListener<EntityExplodeEvent> l@{ event ->
|
||||||
|
entityTracker.untrack(event.entity)
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (world.options.disableExplosions || world.parcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents creepers and tnt minecarts from exploding if explosions are disabled
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from placing liquids, using flint and steel, changing redstone components,
|
||||||
|
* using inputs (unless allowed by the plot),
|
||||||
|
* and using items disabled in the configuration for that world.
|
||||||
|
* Prevents player from using beds in HELL or SKY biomes if explosions are disabled.
|
||||||
|
*/
|
||||||
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event ->
|
||||||
|
val user = event.player
|
||||||
|
val world = worlds.getWorld(user.world) ?: return@l
|
||||||
|
val clickedBlock = event.clickedBlock
|
||||||
|
val parcel = clickedBlock?.let { world.parcelAt(it) }
|
||||||
|
|
||||||
|
if (!user.hasBuildAnywhere && parcel.isPresentAnd { isBanned(user.uuid) }) {
|
||||||
|
user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
when (event.action) {
|
||||||
|
Action.RIGHT_CLICK_BLOCK -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action.RIGHT_CLICK_AIR -> if (event.hasItem()) {
|
||||||
|
val item = event.item.type
|
||||||
|
if (world.options.blockedItems.contains(item)) {
|
||||||
|
user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parcel.canBuildN(user)) {
|
||||||
|
when (item) {
|
||||||
|
LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Action.PHYSICAL -> if (!user.hasBuildAnywhere && !parcel.isPresentAnd { canBuild(user) || allowInteractInputs }) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from breeding mobs, entering or opening boats/minecarts,
|
||||||
|
* rotating item frames, doing stuff with leashes, and putting stuff on armor stands.
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
when (event.rightClicked.type) {
|
||||||
|
EntityType.BOAT,
|
||||||
|
EntityType.MINECART,
|
||||||
|
EntityType.MINECART_CHEST,
|
||||||
|
EntityType.MINECART_COMMAND,
|
||||||
|
EntityType.MINECART_FURNACE,
|
||||||
|
EntityType.MINECART_HOPPER,
|
||||||
|
EntityType.MINECART_MOB_SPAWNER,
|
||||||
|
EntityType.MINECART_TNT,
|
||||||
|
|
||||||
|
EntityType.ARMOR_STAND,
|
||||||
|
EntityType.PAINTING,
|
||||||
|
EntityType.ITEM_FRAME,
|
||||||
|
EntityType.LEASH_HITCH,
|
||||||
|
|
||||||
|
EntityType.CHICKEN,
|
||||||
|
EntityType.COW,
|
||||||
|
EntityType.HORSE,
|
||||||
|
EntityType.SHEEP,
|
||||||
|
EntityType.VILLAGER,
|
||||||
|
EntityType.WOLF -> event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents endermen from griefing.
|
||||||
|
* Prevents sand blocks from exiting the parcel in which they became an entity.
|
||||||
|
*/
|
||||||
|
@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 }) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents portals from being created if set so in the configs for that world
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntityCreatePortalEvent = RegistratorListener<EntityCreatePortalEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (world.options.blockPortalCreation) event.isCancelled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from dropping items
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from picking up items
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from editing inventories
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL, events = ["inventory.InventoryClickEvent", "inventory.InventoryDragEvent"])
|
||||||
|
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) && !allowInteractInventory }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancels weather changes and sets the weather to sunny if requested by the config for that world.
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onWeatherChangeEvent = RegistratorListener<WeatherChangeEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.world) ?: return@l
|
||||||
|
if (world.options.noWeather && event.toWeatherState()) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetWeather(world: World) {
|
||||||
|
world.setStorm(false)
|
||||||
|
world.isThundering = false
|
||||||
|
world.weatherDuration = Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets time to day and doDayLightCycle gamerule if requested by the config for that world
|
||||||
|
* Sets the weather to sunny if requested by the config for that world.
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onWorldLoadEvent = RegistratorListener<WorldLoadEvent> l@{ event ->
|
||||||
|
enforceWorldSettingsIfApplicable(event.world)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enforceWorldSettingsIfApplicable(w: World) {
|
||||||
|
val world = worlds.getWorld(w) ?: return
|
||||||
|
if (world.options.dayTime) {
|
||||||
|
w.setGameRuleValue("doDaylightCycle", "false")
|
||||||
|
w.time = 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world.options.noWeather) {
|
||||||
|
resetWeather(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.setGameRuleValue("doTileDrops", world.options.doTileDrops.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents mobs (living entities) from spawning if that is disabled for that world in the config.
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntitySpawnEvent = RegistratorListener<EntitySpawnEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (event.entity is Creature && world.options.blockMobSpawning) {
|
||||||
|
event.isCancelled = true
|
||||||
|
} else if (world.parcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents minecarts/boats from moving outside a plot
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onVehicleMoveEvent = RegistratorListener<VehicleMoveEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.to.block) ?: return@l
|
||||||
|
if (ppa == null) {
|
||||||
|
event.vehicle.eject()
|
||||||
|
event.vehicle.passengers.forEach {
|
||||||
|
if (it.type == EntityType.PLAYER) {
|
||||||
|
(it as Player).sendParcelMessage(except = true, message = "Your ride ends here")
|
||||||
|
} else it.remove()
|
||||||
|
}
|
||||||
|
} else if (ppa.hasBlockVisitors) {
|
||||||
|
event.to.subtract(event.to).add(event.from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from removing items from item frames
|
||||||
|
* Prevents TNT Minecarts and creepers from destroying entities (This event is called BEFORE EntityExplodeEvent GG)
|
||||||
|
* Actually doesn't prevent this because the entities are destroyed anyway, even though the code works?
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntityDamageByEntityEvent = RegistratorListener<EntityDamageByEntityEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (world.options.disableExplosions && event.damager is ExplosiveMinecart || event.damager is Creeper) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
val user = event.damager as? Player
|
||||||
|
?: (event.damager as? Projectile)?.let { it.shooter as? Player }
|
||||||
|
?: return@l
|
||||||
|
|
||||||
|
if (!world.parcelAt(event.entity).canBuildN(user)) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onHangingBreakEvent = RegistratorListener<HangingBreakEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (event.cause == HangingBreakEvent.RemoveCause.EXPLOSION && world.options.disableExplosions) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world.parcelAt(event.entity).isPresentAnd { hasBlockVisitors }) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from deleting paintings and item frames
|
||||||
|
* This appears to take care of shooting with a bow, throwing snowballs or throwing ender pearls.
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onHangingBreakByEntityEvent = RegistratorListener<HangingBreakByEntityEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
val user = event.remover as? Player ?: return@l
|
||||||
|
if (!world.parcelAt(event.entity).canBuildN(user)) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents players from placing paintings and item frames
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onHangingPlaceEvent = RegistratorListener<HangingPlaceEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
val block = event.block.getRelative(event.blockFace)
|
||||||
|
if (!world.parcelAt(block).canBuildN(event.player)) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents stuff from growing outside of plots
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onStructureGrowEvent = RegistratorListener<StructureGrowEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.location.block) ?: return@l
|
||||||
|
if (ppa == null) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.player.hasBuildAnywhere && !ppa.canBuild(event.player)) {
|
||||||
|
event.isCancelled = true; return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
event.blocks.removeIf { wo.parcelAt(it.block) !== ppa }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents dispensers/droppers from dispensing out of parcels
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onBlockDispenseEvent = RegistratorListener<BlockDispenseEvent> l@{ event ->
|
||||||
|
val block = event.block
|
||||||
|
if (!block.type.let { it == DISPENSER || it == DROPPER }) return@l
|
||||||
|
val world = worlds.getWorld(block.world) ?: return@l
|
||||||
|
val data = block.blockData as Directional
|
||||||
|
val targetBlock = block.getRelative(data.facing)
|
||||||
|
if (world.parcelAt(targetBlock) == null) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Track spawned items, making sure they don't leave the parcel.
|
||||||
|
*/
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents endermen and endermite from teleporting outside their parcel
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntityTeleportEvent = RegistratorListener<EntityTeleportEvent> l@{ event ->
|
||||||
|
val (wo, ppa) = getWoAndPPa(event.from.block) ?: return@l
|
||||||
|
if (ppa !== wo.parcelAt(event.to)) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents projectiles from flying out of parcels
|
||||||
|
* Prevents players from firing projectiles if they cannot build
|
||||||
|
*/
|
||||||
|
@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) {
|
||||||
|
event.isCancelled = true
|
||||||
|
} else {
|
||||||
|
entityTracker.track(event.entity, ppa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevents entities from dropping items upon death, if configured that way
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onEntityDeathEvent = RegistratorListener<EntityDeathEvent> l@{ event ->
|
||||||
|
entityTracker.untrack(event.entity)
|
||||||
|
val world = worlds.getWorld(event.entity.world) ?: return@l
|
||||||
|
if (!world.options.dropEntityItems) {
|
||||||
|
event.drops.clear()
|
||||||
|
event.droppedExp = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assigns players their default game mode upon entering the world
|
||||||
|
*/
|
||||||
|
@field:ListenerMarker(priority = NORMAL)
|
||||||
|
val onPlayerChangedWorldEvent = RegistratorListener<PlayerChangedWorldEvent> l@{ event ->
|
||||||
|
val world = worlds.getWorld(event.player.world) ?: return@l
|
||||||
|
if (world.options.gameMode != null && !event.player.hasGamemodeBypass) {
|
||||||
|
event.player.gameMode = world.options.gameMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,6 +12,51 @@ fun File.tryCreate(): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <R> Any.synchronized(block: () -> R): R {
|
inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
|
||||||
return 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, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
|
||||||
|
return EditLoopScope(this).doEditLoop(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
|
||||||
|
return EditLoopScope(this).doEditLoop(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
|
||||||
|
private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
|
||||||
|
lateinit var _entry: MutableMap.MutableEntry<T, U>
|
||||||
|
|
||||||
|
inline val key get() = _entry.key
|
||||||
|
inline var value
|
||||||
|
get() = _entry.value
|
||||||
|
set(target) = run { _entry.setValue(target) }
|
||||||
|
|
||||||
|
inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
|
||||||
|
val it = _initIterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
_entry = it.next()
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
|
||||||
|
val it = _initIterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
val entry = it.next().also { _entry = it }
|
||||||
|
block(entry.key, entry.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove() {
|
||||||
|
iterator!!.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
|
||||||
|
iterator?.let { throw IllegalStateException() }
|
||||||
|
return _map.entries.iterator().also { iterator = it }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user