diff --git a/src/main/kotlin/xyz/atrius/waystones/service/KeyService.kt b/src/main/kotlin/xyz/atrius/waystones/service/KeyService.kt index e2333f5..c9c4c8e 100644 --- a/src/main/kotlin/xyz/atrius/waystones/service/KeyService.kt +++ b/src/main/kotlin/xyz/atrius/waystones/service/KeyService.kt @@ -11,6 +11,7 @@ import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.inventory.EquipmentSlot import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.CompassMeta +import org.bukkit.persistence.PersistentDataType import org.koin.core.annotation.Single import xyz.atrius.waystones.data.config.property.EnableKeyItemsProperty import xyz.atrius.waystones.data.config.property.PortalSicknessWarpingProperty @@ -41,10 +42,8 @@ class KeyService( } fun isWarpKey(key: ItemStack) = when (enableKeyItems.value()) { - true -> key.itemMeta?.get("is_warp_key") == 1 - else -> - key.type == Material.COMPASS && - (key.itemMeta as? CompassMeta)?.lodestone != null + true -> key.itemMeta?.get("is_warp_key", PersistentDataType.INTEGER) == 1 + else -> key.type == Material.COMPASS && (key.itemMeta as? CompassMeta)?.lodestone != null } private fun validateKey(player: Player, key: ItemStack): Either = either { diff --git a/src/main/kotlin/xyz/atrius/waystones/service/LinkService.kt b/src/main/kotlin/xyz/atrius/waystones/service/LinkService.kt index 6f538cc..e6cb619 100644 --- a/src/main/kotlin/xyz/atrius/waystones/service/LinkService.kt +++ b/src/main/kotlin/xyz/atrius/waystones/service/LinkService.kt @@ -11,6 +11,7 @@ import org.bukkit.block.Block import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.CompassMeta +import org.bukkit.persistence.PersistentDataType import org.koin.core.annotation.Single import xyz.atrius.waystones.data.config.property.RelinkableKeysProperty import xyz.atrius.waystones.manager.LocalizationManager @@ -18,8 +19,10 @@ import xyz.atrius.waystones.manager.LocalizedString import xyz.atrius.waystones.provider.DefaultKeyProvider import xyz.atrius.waystones.repository.WaystoneInfoRepository import xyz.atrius.waystones.utility.addItemNaturally +import xyz.atrius.waystones.utility.get import xyz.atrius.waystones.utility.locationCode import xyz.atrius.waystones.utility.playSound +import xyz.atrius.waystones.utility.toKey import xyz.atrius.waystones.utility.update @Single @@ -39,7 +42,42 @@ class LinkService( val meta = ensureNotNull(item.itemMeta as? CompassMeta) { LinkServiceError.Ignore } + val info = waystoneInfoRepository + .getWaystone(block.location) + .get() + val metaName = meta["waystone_name", PersistentDataType.STRING] + val name = info?.name + // Determine if the key is being relinked to a different waystone + val isDifferentName = name != null && metaName != name + val isSameLocation = block.location == meta.lodestone + // A warp key having an existing waystone name in its metadata implies that the key was already + // linked once before. If this field is present and differs from the name of the waystone present + // on the database, and the location is the same, we can allow the key to be relinked to the waystone. + when (isSameLocation && isDifferentName) { + true -> updateWarpKey(player, item, block, name) + else -> linkNewWarpKey(player, item, block, name, meta).bind() + } + } + private fun updateWarpKey( + player: Player, + item: ItemStack, + block: Block, + name: String?, + ) { + item.link(player, block, name) + player.playSound(Sound.ITEM_LODESTONE_COMPASS_LOCK) + } + + private fun linkNewWarpKey( + player: Player, + item: ItemStack, + block: Block, + name: String?, + meta: CompassMeta, + ): Either = either { + // Check if the key already has a linked location. If relinking is + // disabled, we should not allow the user to relink the key. if (!relinkableKeys.value()) { ensure(!meta.hasLodestone()) { LinkServiceError.NotRelinkable(localization) @@ -52,20 +90,33 @@ class LinkService( // Add item to players inventory val key = defaultKeyProvider .getKey(player) - .link(player, block) + .link(player, block, name) // Add item and play sound player.inventory.addItemNaturally(item, key) player.playSound(Sound.ITEM_LODESTONE_COMPASS_LOCK) } - private fun ItemStack.link(player: Player, block: Block) = update { + private fun ItemStack.link(player: Player, block: Block, name: String?) = update { lodestone = block.location isLodestoneTracked = true - val name = waystoneInfoRepository - .getWaystone(block.location) - .thenApplyAsync { it?.name ?: localization["unnamed-waystone"].format(player) } - val lore = localization["link-key-lore", name.get(), lodestone?.locationCode] + if (!name.isNullOrEmpty()) { + val displayName = localization["key-name"] + .format(player) + .let { "$it: ($name)" } + .let(Component::text) + this.displayName(displayName) + // Ensure the item has the waystone name set in its PDC + // We will need this later for verification if the user attempts to relink the key + persistentDataContainer.set( + "waystone_name".toKey(), + PersistentDataType.STRING, + name, + ) + } + + val name = name ?: localization["unnamed-waystone"].format(player) + val lore = localization["link-key-lore", name, lodestone?.locationCode] .format(player) .let(Component::text) diff --git a/src/main/kotlin/xyz/atrius/waystones/service/NameService.kt b/src/main/kotlin/xyz/atrius/waystones/service/NameService.kt index 66ec41c..d213f10 100644 --- a/src/main/kotlin/xyz/atrius/waystones/service/NameService.kt +++ b/src/main/kotlin/xyz/atrius/waystones/service/NameService.kt @@ -11,8 +11,10 @@ import org.bukkit.block.Block import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.koin.core.annotation.Single +import org.slf4j.LoggerFactory import xyz.atrius.waystones.dao.WaystoneInfo import xyz.atrius.waystones.repository.WaystoneInfoRepository +import xyz.atrius.waystones.utility.locationCode @Single class NameService( @@ -45,10 +47,18 @@ class NameService( val name = PlainTextComponentSerializer .plainText() .serialize(displayName) - val info = WaystoneInfo - .fromLocation(block.location, name) + val info = waystoneInfoRepository + .getWaystone(block.location) + .get() + + ensure(name != info?.name) { + logger.debug("Waystone at ${block.location.locationCode} already has name '$name'! Skipping rename.") + NameServiceError.Ignore + } - waystoneInfoRepository.save(info) + val newInfo = WaystoneInfo + .fromLocation(block.location, name) + waystoneInfoRepository.save(newInfo) if (player.gameMode != GameMode.CREATIVE) { item.amount-- @@ -61,4 +71,10 @@ class NameService( object Ignore : NameServiceError() } + + companion object { + + private val logger = LoggerFactory + .getLogger(NameService::class.java.name) + } } diff --git a/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt b/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt index 0829fc4..eef4a80 100644 --- a/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt +++ b/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt @@ -5,19 +5,14 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.PlayerInventory import org.bukkit.inventory.meta.ItemMeta import org.bukkit.persistence.PersistentDataType -import org.bukkit.persistence.PersistentDataType.INTEGER -operator fun ItemMeta.get(key: String) = - persistentDataContainer.get(key.toKey(), INTEGER) +operator fun ItemMeta.get(key: String, type: PersistentDataType) = + persistentDataContainer + .get(key.toKey(), type) operator fun ItemMeta.set(key: String, type: PersistentDataType, value: T) = persistentDataContainer - .set( - key.toKey(), - type, - value - ?: error("Value must be provided!") - ) + .set(key.toKey(), type, value ?: error("Value must be provided!")) fun PlayerInventory.addItemNaturally(original: ItemStack, new: ItemStack) { val player = holder as Player