feat: 表示のUpdate
This commit is contained in:
parent
79d34ee2dc
commit
bbb88cea8f
63
src/main/kotlin/net/hareworks/hcu/shop/ShopVisuals.kt
Normal file
63
src/main/kotlin/net/hareworks/hcu/shop/ShopVisuals.kt
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package net.hareworks.hcu.shop
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.player.PlayerIdServiceImpl
|
||||||
|
import net.hareworks.hcu.shop.database.ShopData
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import org.bukkit.block.Sign
|
||||||
|
import org.bukkit.block.sign.Side
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
object ShopVisuals {
|
||||||
|
fun updateSign(plugin: JavaPlugin, block: org.bukkit.block.Block, shopData: ShopData, side: Side) {
|
||||||
|
val state = block.state as? Sign ?: return
|
||||||
|
val signSide = state.getSide(side)
|
||||||
|
|
||||||
|
// Fetch item name
|
||||||
|
val itemStack = shopData.item
|
||||||
|
val itemName = if (itemStack != null) {
|
||||||
|
itemStack.type.name.lowercase().split("_")
|
||||||
|
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||||
|
.take(15) // Limit length to fit on sign if needed?
|
||||||
|
} else {
|
||||||
|
"Not Set"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch Owner Name
|
||||||
|
val playerIdService = PlayerIdServiceImpl(plugin.logger)
|
||||||
|
val ownerName = try {
|
||||||
|
val entry = playerIdService.find(shopData.actorId)
|
||||||
|
if (entry != null) {
|
||||||
|
org.bukkit.Bukkit.getOfflinePlayer(entry.uuid).name ?: "Unknown"
|
||||||
|
} else {
|
||||||
|
"Unknown"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
val amount = itemStack?.amount ?: 0
|
||||||
|
val price = shopData.price.toInt()
|
||||||
|
val priceStr = net.hareworks.hcu.economy.api.EconomyService.format(price)
|
||||||
|
|
||||||
|
// Format:
|
||||||
|
// L1: <Item Name> (Aqua)
|
||||||
|
// L2: x<Amount> - <Price Formatted> (Black)
|
||||||
|
// L3: (Empty)
|
||||||
|
// L4: <Owner Name> (Gray)
|
||||||
|
|
||||||
|
signSide.line(0, Component.text(itemName, NamedTextColor.AQUA))
|
||||||
|
signSide.line(1, Component.text("x$amount - $priceStr", NamedTextColor.BLACK))
|
||||||
|
signSide.line(2, Component.empty())
|
||||||
|
signSide.line(3, Component.text(ownerName, NamedTextColor.GRAY))
|
||||||
|
|
||||||
|
// Update the sign one tick later to ensure persistence compatibility
|
||||||
|
// Or if called from command, update immediately?
|
||||||
|
// Safer to always schedule if in doubt, or check constraints.
|
||||||
|
// For command, immediate update is fine, but for event, delay is needed.
|
||||||
|
// Let's us runTask always for safety.
|
||||||
|
plugin.server.scheduler.runTask(plugin, Runnable {
|
||||||
|
state.update()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,6 +50,17 @@ object ShopCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
literal("update-signs") {
|
||||||
|
permission {
|
||||||
|
description = "Force update all shop signs"
|
||||||
|
defaultValue = org.bukkit.permissions.PermissionDefault.OP
|
||||||
|
}
|
||||||
|
executes {
|
||||||
|
val count = updateAllSigns(plugin)
|
||||||
|
sender.sendMessage(Component.text("Updated $count shop signs.", NamedTextColor.GREEN))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,4 +82,21 @@ object ShopCommands {
|
||||||
sender.inventory.addItem(signItem)
|
sender.inventory.addItem(signItem)
|
||||||
sender.sendMessage(Component.text("Given Shop Sign Tier $tier", NamedTextColor.GREEN))
|
sender.sendMessage(Component.text("Given Shop Sign Tier $tier", NamedTextColor.GREEN))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateAllSigns(plugin: ShopPlugin): Int {
|
||||||
|
val shops = net.hareworks.hcu.shop.database.ShopRepository.findAll()
|
||||||
|
var count = 0
|
||||||
|
for (shop in shops) {
|
||||||
|
val world = org.bukkit.Bukkit.getWorld(shop.worldUid) ?: continue
|
||||||
|
val block = world.getBlockAt(shop.x, shop.y, shop.z)
|
||||||
|
// Ideally we check if loaded, but getBlockAt loads chunk if not async?
|
||||||
|
// In main thread command, it's fine for now, but beware of lag with thousands of shops.
|
||||||
|
if (block.type == Material.OAK_SIGN) {
|
||||||
|
val side = if (shop.face == "FRONT") org.bukkit.block.sign.Side.FRONT else org.bukkit.block.sign.Side.BACK
|
||||||
|
net.hareworks.hcu.shop.ShopVisuals.updateSign(plugin, block, shop, side)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,13 @@ object ShopRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findAll(): List<ShopData> {
|
||||||
|
return HcuDatabase.transaction {
|
||||||
|
Shops.selectAll()
|
||||||
|
.map { toShopData(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateStock(id: Int, newStock: Int) {
|
fun updateStock(id: Int, newStock: Int) {
|
||||||
HcuDatabase.transaction {
|
HcuDatabase.transaction {
|
||||||
Shops.update({ Shops.id eq id }) {
|
Shops.update({ Shops.id eq id }) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package net.hareworks.hcu.shop.listeners
|
package net.hareworks.hcu.shop.listeners
|
||||||
import net.hareworks.hcu.shop.ShopPlugin
|
import net.hareworks.hcu.shop.ShopPlugin
|
||||||
|
import net.hareworks.hcu.shop.ShopVisuals
|
||||||
import net.hareworks.hcu.shop.database.ShopRepository
|
import net.hareworks.hcu.shop.database.ShopRepository
|
||||||
import net.hareworks.hcu.core.player.PlayerIdServiceImpl
|
import net.hareworks.hcu.core.player.PlayerIdServiceImpl
|
||||||
import net.kyori.adventure.text.Component
|
import net.kyori.adventure.text.Component
|
||||||
|
|
@ -57,16 +58,16 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
val signState = event.blockPlaced.state as? Sign ?: return
|
val signState = event.blockPlaced.state as? Sign ?: return
|
||||||
|
|
||||||
// Front Text
|
// Front Text
|
||||||
|
// Handled by ShopVisuals later
|
||||||
|
// backSide.line(0, Component.text("Back")) ...
|
||||||
|
|
||||||
|
// Just ensure color is black initially if needed, but Visuals will overwrite.
|
||||||
val frontSide = signState.getSide(Side.FRONT)
|
val frontSide = signState.getSide(Side.FRONT)
|
||||||
frontSide.line(0, Component.text("Front"))
|
|
||||||
frontSide.line(1, Component.text("oak_log"))
|
|
||||||
frontSide.color = DyeColor.BLACK
|
frontSide.color = DyeColor.BLACK
|
||||||
frontSide.isGlowingText = false
|
frontSide.isGlowingText = false
|
||||||
|
|
||||||
// Back Text
|
// Back Text
|
||||||
val backSide = signState.getSide(Side.BACK)
|
val backSide = signState.getSide(Side.BACK)
|
||||||
backSide.line(0, Component.text("Back"))
|
|
||||||
backSide.line(1, Component.text("gunpowder"))
|
|
||||||
backSide.color = DyeColor.BLACK
|
backSide.color = DyeColor.BLACK
|
||||||
backSide.isGlowingText = false
|
backSide.isGlowingText = false
|
||||||
|
|
||||||
|
|
@ -88,18 +89,24 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
val actorId = playerEntry.actorId
|
val actorId = playerEntry.actorId
|
||||||
|
|
||||||
// Create FRONT shop
|
// Create FRONT shop
|
||||||
ShopRepository.create(
|
val frontShop = ShopRepository.create(
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
location = event.blockPlaced.location,
|
location = event.blockPlaced.location,
|
||||||
face = "FRONT"
|
face = "FRONT"
|
||||||
)
|
)
|
||||||
|
if (frontShop != null) {
|
||||||
|
ShopVisuals.updateSign(plugin, event.blockPlaced, frontShop, Side.FRONT)
|
||||||
|
}
|
||||||
|
|
||||||
// Create BACK shop
|
// Create BACK shop
|
||||||
ShopRepository.create(
|
val backShop = ShopRepository.create(
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
location = event.blockPlaced.location,
|
location = event.blockPlaced.location,
|
||||||
face = "BACK"
|
face = "BACK"
|
||||||
)
|
)
|
||||||
|
if (backShop != null) {
|
||||||
|
ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn Displays
|
// Spawn Displays
|
||||||
|
|
@ -223,22 +230,14 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
player.sendMessage(Component.text("Shop price set to $$price", NamedTextColor.GREEN))
|
player.sendMessage(Component.text("Shop price set to $$price", NamedTextColor.GREEN))
|
||||||
|
|
||||||
// Update Actual Sign Visuals
|
// Update Actual Sign Visuals
|
||||||
updateShopSignVisuals(event.block, shopData.copy(price = price), event.side)
|
ShopVisuals.updateSign(plugin, event.block, shopData.copy(price = price), event.side)
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(Component.text("Invalid price format. Please enter a valid number.", NamedTextColor.RED))
|
player.sendMessage(Component.text("Invalid price format. Please enter a valid number.", NamedTextColor.RED))
|
||||||
// Optionally reopen? For now just fail.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up Map
|
||||||
ShopInputMap.pendingInputs.remove(player.uniqueId)
|
ShopInputMap.pendingInputs.remove(player.uniqueId)
|
||||||
|
|
||||||
// Cancel the raw update so we don't just paste "100" on the sign
|
|
||||||
// But we DO want to update it with the visual method above.
|
|
||||||
// Since we called updateShopSignVisuals which does state.update(),
|
|
||||||
// verifying if cancelling event interferes.
|
|
||||||
// Usually, event changes the state *after* this handler.
|
|
||||||
// If we cancel, no change happens.
|
|
||||||
// So we cancel, and manually set the text we want.
|
|
||||||
event.isCancelled = true
|
event.isCancelled = true
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -251,34 +250,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateShopSignVisuals(block: org.bukkit.block.Block, shopData: net.hareworks.hcu.shop.database.ShopData, side: Side) {
|
|
||||||
val state = block.state as? Sign ?: return
|
|
||||||
val signSide = state.getSide(side) // Currently updating the side being edited
|
|
||||||
|
|
||||||
// Fetch item name
|
|
||||||
val itemName = shopData.item?.type?.name?.lowercase()?.replace("_", " ") ?: "Unknown"
|
|
||||||
|
|
||||||
// Format:
|
|
||||||
// L1: <Item Name>
|
|
||||||
// L2: Buy: <Price>
|
|
||||||
// L3: Stock: <Stock>
|
|
||||||
|
|
||||||
signSide.line(0, Component.text(itemName, NamedTextColor.BLACK))
|
|
||||||
signSide.line(1, Component.text("Buy: $${shopData.price}", NamedTextColor.BLACK))
|
|
||||||
signSide.line(2, Component.text("Stock: ${shopData.stock}", NamedTextColor.BLACK))
|
|
||||||
signSide.line(3, Component.empty())
|
|
||||||
|
|
||||||
// If updating the sign block during event cancellation, we might need a scheduler or force update
|
|
||||||
// But since we are cancelling the event, the state remains as is.
|
|
||||||
// We need to apply THESE changes.
|
|
||||||
// state.update() pushes to world.
|
|
||||||
|
|
||||||
// Wait, if we are in SignChangeEvent, the block is currently processing update.
|
|
||||||
// It's safer to run a task to update it 1 tick later if we cancel the event.
|
|
||||||
plugin.server.scheduler.runTask(plugin, Runnable {
|
|
||||||
state.update()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onShopInteract(event: PlayerInteractEvent) {
|
fun onShopInteract(event: PlayerInteractEvent) {
|
||||||
|
|
@ -415,10 +387,16 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
}
|
}
|
||||||
inventory.setItem(2, barrier)
|
inventory.setItem(2, barrier)
|
||||||
|
|
||||||
// Update Display
|
// Update Display and Sign
|
||||||
val updatedShop = ShopRepository.findById(shopId)
|
val updatedShop = ShopRepository.findById(shopId)
|
||||||
if (updatedShop != null) {
|
if (updatedShop != null) {
|
||||||
updateShopDisplay(updatedShop)
|
updateShopDisplay(updatedShop)
|
||||||
|
val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid)
|
||||||
|
val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z)
|
||||||
|
if (block != null && block.type == Material.OAK_SIGN) {
|
||||||
|
val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK
|
||||||
|
ShopVisuals.updateSign(plugin, block, updatedShop, side)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,6 +417,12 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
val updatedShop = ShopRepository.findById(shopId)
|
val updatedShop = ShopRepository.findById(shopId)
|
||||||
if (updatedShop != null) {
|
if (updatedShop != null) {
|
||||||
updateShopDisplay(updatedShop)
|
updateShopDisplay(updatedShop)
|
||||||
|
val world = org.bukkit.Bukkit.getWorld(updatedShop.worldUid)
|
||||||
|
val block = world?.getBlockAt(updatedShop.x, updatedShop.y, updatedShop.z)
|
||||||
|
if (block != null && block.type == Material.OAK_SIGN) {
|
||||||
|
val side = if (updatedShop.face == "FRONT") Side.FRONT else Side.BACK
|
||||||
|
ShopVisuals.updateSign(plugin, block, updatedShop, side)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt for Price via Sign
|
// Prompt for Price via Sign
|
||||||
|
|
@ -484,13 +468,6 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
|
|
||||||
// Determine side based on ShopData face
|
// Determine side based on ShopData face
|
||||||
val side = if (shopData.face == "FRONT") Side.FRONT else Side.BACK
|
val side = if (shopData.face == "FRONT") Side.FRONT else Side.BACK
|
||||||
val signSide = sign.getSide(side)
|
|
||||||
|
|
||||||
// Send client-side update with prompt
|
|
||||||
// Using Paper API: player.sendBlockUpdate(location, blockData) doesnt allow setting text easily
|
|
||||||
// We can modify the sign state locally and send update, then revert?
|
|
||||||
// Or actually, `player.sendBlockUpdate` takes a `TileState` in recent Paper versions?
|
|
||||||
// If not, we can use `player.sendSignChange`.
|
|
||||||
|
|
||||||
val promptLines = listOf(
|
val promptLines = listOf(
|
||||||
Component.text("Price : ", NamedTextColor.BLACK),
|
Component.text("Price : ", NamedTextColor.BLACK),
|
||||||
|
|
@ -499,7 +476,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
||||||
Component.empty()
|
Component.empty()
|
||||||
)
|
)
|
||||||
|
|
||||||
player.sendSignChange(location, promptLines, DyeColor.BLACK, true) // hasGlowingText=true/false?
|
player.sendSignChange(location, promptLines, DyeColor.BLACK, true)
|
||||||
|
|
||||||
// Register pending input
|
// Register pending input
|
||||||
ShopInputMap.pendingInputs[player.uniqueId] = shopId
|
ShopInputMap.pendingInputs[player.uniqueId] = shopId
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user