Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/main/kotlin/xyz/atrius/waystones/event/WarpEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import xyz.atrius.waystones.manager.AdvancementManager
import xyz.atrius.waystones.manager.LocalizationManager
import xyz.atrius.waystones.service.KeyService
import xyz.atrius.waystones.service.TeleportService
import xyz.atrius.waystones.service.WarpValidationService
import xyz.atrius.waystones.service.WaystoneService
import xyz.atrius.waystones.utility.cancel
import xyz.atrius.waystones.utility.foldResult
Expand All @@ -37,16 +38,18 @@ class WarpEvent(
private val advancementManager: AdvancementManager,
private val secretTunnelAdvancement: SecretTunnelAdvancement,
private val shootTheMessenger: ShootTheMessengerAdvancement,
private val warpValidationService: WarpValidationService,
) : Listener {

@EventHandler
fun onClick(event: PlayerInteractEvent) {
val player = event.player
// Don't start warp while flying with elytra, not right-clicking, or a lodestone was clicked
if (player.isGliding ||
!event.action.isRightClick ||
waystoneService.isWaystone(event.clickedBlock)
) {
// We only care about right-click actions
if (!event.action.isRightClick) {
return
}

if (!warpValidationService.validateCanWarp(player, event.clickedBlock)) {
return
}
// Make sure the key is connected before we continue
Expand Down
172 changes: 172 additions & 0 deletions src/main/kotlin/xyz/atrius/waystones/service/WarpValidationService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package xyz.atrius.waystones.service

import org.bukkit.Material
import org.bukkit.block.Bed
import org.bukkit.block.Block
import org.bukkit.block.BlockState
import org.bukkit.block.ShulkerBox
import org.bukkit.block.data.BlockData
import org.bukkit.block.data.type.ChiseledBookshelf
import org.bukkit.block.data.type.Door
import org.bukkit.block.data.type.Gate
import org.bukkit.block.data.type.HangingSign
import org.bukkit.block.data.type.Shelf
import org.bukkit.block.data.type.Sign
import org.bukkit.block.data.type.Switch
import org.bukkit.block.data.type.TrapDoor
import org.bukkit.block.data.type.WallHangingSign
import org.bukkit.block.data.type.WallSign
import org.bukkit.entity.Player
import org.koin.core.annotation.Single
import org.slf4j.LoggerFactory

@Single
class WarpValidationService(
private val waystoneService: WaystoneService,
) {
// We don't have an easy way to check if a player is clicking on a block that opens a GUI,
// so we maintain a list of blocks that open a GUI when interacted with.
private val guiBlocks = setOf(
Material.ANVIL,
Material.BARREL,
Material.BEACON,
Material.CHIPPED_ANVIL,
Material.CRAFTING_TABLE,
Material.BLAST_FURNACE,
Material.BREWING_STAND,
Material.CARTOGRAPHY_TABLE,
Material.CHEST,
Material.COMMAND_BLOCK,
Material.CHAIN_COMMAND_BLOCK,
Material.REPEATING_COMMAND_BLOCK,
Material.COPPER_CHEST,
Material.CRAFTER,
Material.CRAFTING_TABLE,
Material.DAMAGED_ANVIL,
Material.DISPENSER,
Material.DROPPER,
Material.ENCHANTING_TABLE,
Material.ENDER_CHEST,
Material.FURNACE,
Material.GRINDSTONE,
Material.HOPPER,
Material.LECTERN,
Material.LOOM,
Material.SMITHING_TABLE,
Material.SMOKER,
Material.STONECUTTER,
Material.STRUCTURE_BLOCK,
Material.TEST_BLOCK,
Material.TEST_INSTANCE_BLOCK,
Material.TRAPPED_CHEST,
)

// There are some blocks which do not have a GUI but are still interactable and
// may interfere with warping if the player is trying to interact.
private val interactableBlocks = setOf(
Material.BELL,
Material.COMPARATOR,
Material.DAYLIGHT_DETECTOR,
Material.DECORATED_POT,
Material.LEVER,
Material.NOTE_BLOCK,
Material.REPEATER,
)

fun validateCanWarp(player: Player, block: Block?): Boolean {
// Players shouldn't be able to warp while gliding using an elytra, as they would be constantly moving
if (player.isGliding) {
logValidationFailure(player, "Player is gliding with elytra")
return false
}
// Don't allow warp if we are clicking on a waystone, as that would interfere with linking
if (waystoneService.isWaystone(block)) {
return false
}
// Check block state
if (!validateBlockState(player, block?.state)) {
return false
}
// Check block data
if (!validateBlockData(player, block?.blockData)) {
return false
}

if (!validateBlockType(player, block)) {
return false
}

logger.debug("Warp validation succeeded for ${player.name}")
return true
}

private fun validateBlockData(player: Player, data: BlockData?): Boolean = when (data) {
// Check if block is any sign type
is Sign, is HangingSign, is WallSign, is WallHangingSign -> {
logValidationFailure(player, "Player is interacting with a sign")
false
}
// Check if block is any door type
is Door, is TrapDoor, is Gate -> {
logValidationFailure(player, "Player is interacting with a door, trapdoor, or gate")
false
}
// This should primarily catch levers and buttons
is Switch -> {
logValidationFailure(player, "Player is interacting with a switch or button")
false
}

is Shelf -> {
logValidationFailure(player, "Player is interacting with a shelf")
false
}

is ChiseledBookshelf -> {
logValidationFailure(player, "Player is interacting with a chiseled bookshelf")
false
}

else -> true
}

private fun validateBlockState(player: Player, state: BlockState?): Boolean = when (state) {
is Bed -> {
logValidationFailure(player, "Player is on a bed")
false
}

is ShulkerBox -> {
logValidationFailure(player, "Player is viewing the contents of a shulker box")
false
}

else -> true
}

private fun validateBlockType(player: Player, block: Block?): Boolean = when (block?.type) {
// Don't warp if an interface is open. This prevents the player from accidentally warping when interacting with
// a block that has a GUI screen. This prioritizes the player's intent to interact with the GUI over warping.
in guiBlocks -> {
logValidationFailure(player, "Player is viewing GUI of ${block?.type}")
false
}
// Don't warp if the player is interacting with an interactable block that we track
in interactableBlocks -> {
logValidationFailure(player, "Player is interacting with ${block?.type}")
false
}

else -> true
}

private fun logValidationFailure(player: Player, message: String) {
logger.debug("Warp validation failed for ${player.name}: $message")
}

companion object {

private val logger = LoggerFactory
.getLogger(WarpValidationService::class.java)
}
}