Skip to content

Commit

Permalink
✨ Add /maplist feature
Browse files Browse the repository at this point in the history
  • Loading branch information
acrylic-style committed Nov 9, 2024
1 parent d510eb0 commit e71b577
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 5 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = "net.azisaba"
version = "6.17.4+1.15.2"
version = "6.18.0+1.15.2"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/github/mori01231/lifecore/DBConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public static void init(@NotNull LifeCore plugin) throws SQLException {
" `word` VARCHAR(255) NOT NULL," +
" UNIQUE KEY `id_word` (`id`, `word`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS `player_maps` (" +
" `owner` VARCHAR(36) NOT NULL," +
" `name` VARCHAR(128) NOT NULL," +
" `data` MEDIUMBLOB NOT NULL," +
" `amount` BIGINT NOT NULL DEFAULT 1," +
" `hash` VARCHAR(256) NOT NULL," +
" PRIMARY KEY (`owner`, `hash`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;");
});
}

Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/github/mori01231/lifecore/LifeCore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.github.mori01231.lifecore.config.*
import com.github.mori01231.lifecore.data.DataLoader
import com.github.mori01231.lifecore.gui.CommandListScreen
import com.github.mori01231.lifecore.gui.DropProtectScreen
import com.github.mori01231.lifecore.gui.MapListScreen
import com.github.mori01231.lifecore.gui.TrashProtectScreen
import com.github.mori01231.lifecore.listener.*
import com.github.mori01231.lifecore.listener.item.*
Expand Down Expand Up @@ -153,6 +154,7 @@ class LifeCore : JavaPlugin() {
registerCommand("gclistenerrestartextendtimecommand", GCListenerRestartExtendTimeCommand(this))
registerCommand("lifecoreutil", LifeCoreUtilCommand(this))
registerCommand("commandlist", CommandListCommand)
registerCommand("maplist", MapListCommand)
registerCommand("respawn") { _, _, _, args ->
args.getOrNull(0)?.let { Bukkit.getPlayerExact(it)?.spigot()?.respawn() }
true
Expand Down Expand Up @@ -253,22 +255,29 @@ class LifeCore : JavaPlugin() {
if (pipeline["lifecore"] != null) {
pipeline.remove("lifecore")
}
} catch (ignored: NoSuchElementException) {
} catch (_: NoSuchElementException) {
}
}
}
logger.info("LifeCore has been disabled.")
}

private fun preloadClasses() {
for (i in 0..5) {
for (i in 0..10) {
preloadClass("com.github.mori01231.lifecore.LifeCore\$onDisable\$$i", false)
}
preloadClass("com.github.mori01231.lifecore.lib.com.charleskorn.kaml.Yaml\$encodeToString\$writer\$1")
preloadClass("com.github.mori01231.lifecore.lib.com.charleskorn.kaml.YamlOutput")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.Yaml")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.nodes.CollectionNode")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.nodes.SequenceNode")
preloadClass("com.github.mori01231.lifecore.lib.org.snakeyaml.engine.v2.api.DumpSettings")
preloadClass("com.github.mori01231.lifecore.lib.org.snakeyaml.engine.v2.api.DumpSettingsBuilder")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.nodes.MappingNode")
preloadClass("com.github.mori01231.lifecore.lib.org.snakeyaml.engine.v2.serializer.NumberAnchorGenerator")
preloadClass("com.github.mori01231.lifecore.lib.org.snakeyaml.engine.v2.common.NonPrintableStyle")
preloadClass("com.github.mori01231.lifecore.lib.org.snakeyaml.engine.v2.emitter.Emitter")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.constructor.ConstructorException")
}

private fun preloadClass(name: String, required: Boolean = true) {
Expand Down Expand Up @@ -319,6 +328,7 @@ class LifeCore : JavaPlugin() {
pm.registerEvents(PromptSignListener, this)
pm.registerEvents(PicksawItemListener(dataLoader), this)
pm.registerEvents(BlockListener, this)
pm.registerEvents(MapListScreen.EventListener, this)

// Items
pm.registerEvents(OreOnlyItemListener(), this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,9 @@ class LifeCoreUtilCommand(val plugin: LifeCore) : TabExecutor {
},
SaveMapData("地図をサーバー移動可能な形に変換します") {
override fun execute(plugin: LifeCore, player: CommandSender, args: Array<String>) {
val meta = (player as Player).inventory.itemInMainHand.itemMeta as? MapMeta? ?: return player.sendMessage("this is not a map")
val meta = (player as Player).inventory.itemInMainHand.itemMeta as? MapMeta? ?: return player.sendMessage("地図を手に持って再度実行してください")
val mapView = meta.mapView ?: return player.sendMessage("mapView is null")
if (mapView.renderers.getOrNull(0) !is CraftMapRenderer) return player.sendMessage("renderers[0] is not an instance of CraftMapRenderer")
if (mapView.renderers.getOrNull(0) !is CraftMapRenderer) return player.sendMessage("すでに変換済みのようです")
if (mapView is CraftMapView) mapView.render(player as CraftPlayer)
val canvas =
mapView.getCanvases()[mapView.renderers.first()]?.get(player as CraftPlayer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.mori01231.lifecore.command

import com.github.mori01231.lifecore.gui.MapListScreen
import org.bukkit.entity.Player

object MapListCommand : PlayerTabExecutor() {
override fun execute(player: Player, args: Array<out String>): Boolean {
MapListScreen(player).openAsync()
return true
}
}
236 changes: 236 additions & 0 deletions src/main/java/com/github/mori01231/lifecore/gui/MapListScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
package com.github.mori01231.lifecore.gui

import com.github.mori01231.lifecore.LifeCore
import com.github.mori01231.lifecore.util.ItemUtil
import com.github.mori01231.lifecore.util.MapDatabaseUtil
import com.github.mori01231.lifecore.util.PromptSign
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.InventoryHolder
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.java.JavaPlugin
import java.util.UUID
import kotlin.math.min

class MapListScreen(val player: Player) : InventoryHolder {
private val inventory = Bukkit.createInventory(this, 54, "${ChatColor.BLUE}マップリスト")
private val itemsInCurrentPage = mutableListOf<MapDatabaseUtil.ItemData>()
@set:JvmName("setPage0")
var page = 0

override fun getInventory() = inventory

fun async(callback: () -> Unit) {
Bukkit.getScheduler().runTaskAsynchronously(JavaPlugin.getPlugin(LifeCore::class.java), Runnable {
callback()
})
}

fun sync(callback: () -> Unit) {
Bukkit.getScheduler().runTask(JavaPlugin.getPlugin(LifeCore::class.java), Runnable {
callback()
})
}

fun resetAsync() {
async {
reset()
}
}

fun reset() {
inventory.clear()
itemsInCurrentPage.clear()
val items = MapDatabaseUtil.getAll(player)
val start = page * 45
val end = (page + 1) * 45
for (i in start until end) {
if (i >= items.size) break
val item = items[i]
inventory.setItem(i - start, createMenuItem(item))
itemsInCurrentPage.add(item)
}
inventory.setItem(45, createGenericItemStack(Material.ARROW, "${ChatColor.YELLOW}前のページ"))
inventory.setItem(49, ItemUtil.createItemStack(Material.NETHER_STAR, "${ChatColor.BLUE}ⓘ このページはなに?", listOf(
"${ChatColor.WHITE}保存したマップの一覧がここに表示されています。",
"${ChatColor.WHITE}それぞれのマップにカーソルを合わせると詳細が表示されます。",
"${ChatColor.WHITE}インベントリ内のマップでShift+クリックすると",
"${ChatColor.WHITE}名前を入力した後にこの画面にマップが追加されます。",
)))
inventory.setItem(53, createGenericItemStack(Material.ARROW, "${ChatColor.YELLOW}次のページ"))
}

fun createGenericItemStack(material: Material, name: String) =
ItemStack(material, 1).apply {
val meta = itemMeta
meta.setDisplayName(name)
itemMeta = meta
}

fun createMenuItem(itemData: MapDatabaseUtil.ItemData) =
ItemStack(Material.FILLED_MAP, 1).apply {
val meta = itemMeta
meta.setDisplayName(itemData.name)
val lore = mutableListOf(
"${ChatColor.YELLOW}アイテム数: ${if (itemData.amount > 0) ChatColor.GREEN else ChatColor.RED}${itemData.amount}",
"",
"${ChatColor.YELLOW}✦ 左クリックで1個入手 (Shiftで64個)",
"${ChatColor.YELLOW}✎ 右クリックで名前を編集"
)
if (itemData.amount <= 0L) {
lore.add("${ChatColor.YELLOW}✖ Shift+右クリックで削除")
} else {
lore.add("${ChatColor.YELLOW}✦ Shift+右クリックですべて取り出す")
}
meta.lore = lore
meta.addItemFlags(ItemFlag.HIDE_DESTROYS)
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS)
meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE)
meta.addItemFlags(ItemFlag.HIDE_PLACED_ON)
meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS)
itemMeta = meta
}

fun getPreviousPage() = if (page == 0) 0 else page - 1

fun getNextPage(): Int {
val items = MapDatabaseUtil.getAll(player)
val maxPage = (items.size - 1) / 45
return if (page == maxPage) maxPage else page + 1
}

fun setPage(page: Int) {
this.page = page
resetAsync()
}

fun openAsync() {
async {
reset()
sync {
player.openInventory(inventory)
}
}
}

object EventListener : Listener {
private val processing = mutableSetOf<UUID>()

@EventHandler
fun onInventoryClick(e: InventoryClickEvent) {
val holder = e.inventory.holder
if (holder !is MapListScreen) return
e.isCancelled = true
val player = e.whoClicked as? Player ?: return
if (processing.contains(player.uniqueId)) return
if (e.clickedInventory != holder.inventory) {
val currentItem = e.currentItem
if (currentItem?.type != Material.FILLED_MAP) return
if (e.click != ClickType.SHIFT_LEFT && e.click != ClickType.SHIFT_RIGHT) return
ItemUtil.getByteArrayTag(currentItem, "SerializedMapData")?.let { if (it.isEmpty()) null else true } ?: run {
player.closeInventory()
player.sendMessage("${ChatColor.RED}先に${ChatColor.AQUA}/lifecoreutil saveMapData${ChatColor.RED}でマップデータを保存してください")
return
}
e.currentItem = null
holder.async {
MapDatabaseUtil.save(player, currentItem) {
holder.openAsync()
}
}
return
}
if (e.slot == 45) {
holder.async {
holder.setPage(holder.getPreviousPage())
}
return
} else if (e.slot == 53) {
holder.async {
holder.setPage(holder.getNextPage())
}
return
}
val itemData = holder.itemsInCurrentPage.getOrNull(e.slot) ?: return
fun takeItem(amount: Int) {
processing.add(player.uniqueId)
holder.async {
val item = try {
MapDatabaseUtil.take(player, itemData.data, amount)
} catch (e: Exception) {
player.sendMessage("${ChatColor.RED}アイテムが足りません。 ${ChatColor.DARK_GRAY}(${e.message})")
return@async
} finally {
processing.remove(player.uniqueId)
}
holder.sync {
player.inventory.addItem(item)
holder.resetAsync()
}
}
}
when (e.click) {
ClickType.LEFT -> {
if (player.inventory.firstEmpty() == -1) {
player.sendMessage("${ChatColor.RED}インベントリがいっぱいです")
return
}
takeItem(1)
}
ClickType.SHIFT_LEFT -> {
if (player.inventory.firstEmpty() == -1) {
player.sendMessage("${ChatColor.RED}インベントリがいっぱいです")
return
}
takeItem(min(64, itemData.amount.toInt()))
}
ClickType.RIGHT -> {
PromptSign.promptSign(player) { lines ->
val name = ChatColor.translateAlternateColorCodes('&', lines.joinToString(""))
MapDatabaseUtil.updateName(player, itemData.data, name)
holder.openAsync()
}
}
ClickType.SHIFT_RIGHT -> {
if (itemData.amount <= 0) {
holder.async {
MapDatabaseUtil.delete(player, itemData.data)
holder.resetAsync()
}
} else {
val emptySpace = player.inventory.contents.count {
@Suppress("SENSELESS_COMPARISON")
it == null || it.type == Material.AIR
}
if (emptySpace == 0) {
player.sendMessage("${ChatColor.RED}インベントリがいっぱいです")
return
}
val amount = if (itemData.amount.toInt() < 0) {
5000
} else {
itemData.amount.toInt()
}
takeItem(min(amount, emptySpace * 64))
}
}
else -> {}
}
}

@EventHandler
fun onInventoryDrag(e: InventoryClickEvent) {
val holder = e.inventory.holder
if (holder !is MapListScreen) return
e.isCancelled = true
}
}
}
Loading

0 comments on commit e71b577

Please sign in to comment.