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.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) {
|
||||
HcuDatabase.transaction {
|
||||
Shops.update({ Shops.id eq id }) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package net.hareworks.hcu.shop.listeners
|
||||
import net.hareworks.hcu.shop.ShopPlugin
|
||||
import net.hareworks.hcu.shop.ShopVisuals
|
||||
import net.hareworks.hcu.shop.database.ShopRepository
|
||||
import net.hareworks.hcu.core.player.PlayerIdServiceImpl
|
||||
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
|
||||
|
||||
// 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)
|
||||
frontSide.line(0, Component.text("Front"))
|
||||
frontSide.line(1, Component.text("oak_log"))
|
||||
frontSide.color = DyeColor.BLACK
|
||||
frontSide.isGlowingText = false
|
||||
|
||||
// Back Text
|
||||
val backSide = signState.getSide(Side.BACK)
|
||||
backSide.line(0, Component.text("Back"))
|
||||
backSide.line(1, Component.text("gunpowder"))
|
||||
backSide.color = DyeColor.BLACK
|
||||
backSide.isGlowingText = false
|
||||
|
||||
|
|
@ -88,18 +89,24 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
val actorId = playerEntry.actorId
|
||||
|
||||
// Create FRONT shop
|
||||
ShopRepository.create(
|
||||
val frontShop = ShopRepository.create(
|
||||
actorId = actorId,
|
||||
location = event.blockPlaced.location,
|
||||
face = "FRONT"
|
||||
)
|
||||
if (frontShop != null) {
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, frontShop, Side.FRONT)
|
||||
}
|
||||
|
||||
// Create BACK shop
|
||||
ShopRepository.create(
|
||||
val backShop = ShopRepository.create(
|
||||
actorId = actorId,
|
||||
location = event.blockPlaced.location,
|
||||
face = "BACK"
|
||||
)
|
||||
if (backShop != null) {
|
||||
ShopVisuals.updateSign(plugin, event.blockPlaced, backShop, Side.BACK)
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn Displays
|
||||
|
|
@ -223,22 +230,14 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
player.sendMessage(Component.text("Shop price set to $$price", NamedTextColor.GREEN))
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
// 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
|
||||
|
||||
} 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
|
||||
fun onShopInteract(event: PlayerInteractEvent) {
|
||||
|
|
@ -415,10 +387,16 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
}
|
||||
inventory.setItem(2, barrier)
|
||||
|
||||
// Update Display
|
||||
// Update Display and Sign
|
||||
val updatedShop = ShopRepository.findById(shopId)
|
||||
if (updatedShop != null) {
|
||||
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)
|
||||
if (updatedShop != null) {
|
||||
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
|
||||
|
|
@ -484,13 +468,6 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
|
||||
// Determine side based on ShopData face
|
||||
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(
|
||||
Component.text("Price : ", NamedTextColor.BLACK),
|
||||
|
|
@ -499,7 +476,7 @@ class ShopListener(private val plugin: ShopPlugin) : Listener {
|
|||
Component.empty()
|
||||
)
|
||||
|
||||
player.sendSignChange(location, promptLines, DyeColor.BLACK, true) // hasGlowingText=true/false?
|
||||
player.sendSignChange(location, promptLines, DyeColor.BLACK, true)
|
||||
|
||||
// Register pending input
|
||||
ShopInputMap.pendingInputs[player.uniqueId] = shopId
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user