1.0.0.3 - new modules

Added utility, menu, thirdparty and exposed modules
A few little fixes
This commit is contained in:
redtea committed 2024-03-28 10:12:38 +03:00
1 parent 161f9aa268
commit 3c87e2d6a1
63 files changed
+5334 -12

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
+117
View File
@@ -0,0 +1,117 @@
plugins {
id("java")
kotlin("jvm")
kotlin("plugin.serialization") version "1.8.21"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("maven-publish")
id("signing")
}
group = "tech.carcadex"
val globalArtifactId = "kotlinbukkitkit-menu"
val globalName = "kotlinbukkitkit-menu"
repositories {
mavenCentral()
maven {
name = "spigotmc-repo"
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
}
dependencies {
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
api(project(":architecture"))
api(project(":extensions"))
api(project(":utility"))
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(8)
}
kotlin {
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
artifactId = globalArtifactId
groupId = "tech.carcadex"
version = version
from(components["java"])
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
pom {
artifactId = globalArtifactId
groupId = "tech.carcadex"
version = version
name.set(globalArtifactId)
description.set(globalArtifactId)
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
licenses {
license {
name.set("MIT")
url.set("https://mit-license.org/")
}
}
developers {
developer {
id.set("itzRedTea")
name.set("Red Tea")
email.set("red__tea@outlook.com")
}
}
scm {
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
}
}
}
}
repositories {
maven {
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
username = "itzRedTea"
password = project.findProperty("password").toString()
}
}
}
}
signing {
useGpgCmd()
sign(publishing.publications.getByName("maven"))
}
@@ -0,0 +1,51 @@
package tech.carcadex.kotlinbukkitkit.menu
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryHolder
import org.bukkit.plugin.Plugin
import java.util.*
public interface Menu<T : Slot> : WithPlugin<Plugin>, InventoryHolder {
public var title: String
public var lines: Int
public var cancelOnClick: Boolean
public val viewers: Map<Player, Inventory>
public val slots: TreeMap<Int, T>
public val data: WeakHashMap<String, Any>
public val playerData: WeakHashMap<Player, WeakHashMap<String, Any>>
public val eventHandler: MenuEventHandler
public var baseSlot: T
public var updateDelay: Long
public fun setSlot(slot: Int, slotObj: T)
public fun update(players: Set<Player> = viewers.keys)
public fun updateSlot(slot: Slot, players: Set<Player> = viewers.keys)
public fun update(vararg players: Player): Unit = update(players.toSet())
public fun updateSlot(slot: Slot, vararg players: Player): Unit = updateSlot(slot, players.toSet())
public fun openToPlayer(vararg players: Player)
public fun clearData() {
data.clear()
for (slot in slotsWithBaseSlot())
slot.clearSlotData()
}
public fun clearPlayerData(player: Player) {
playerData.remove(player)
for (slot in slotsWithBaseSlot())
slot.clearPlayerData(player)
}
public fun close(player: Player, closeInventory: Boolean = true)
}
@@ -0,0 +1,105 @@
package tech.carcadex.kotlinbukkitkit.menu
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAware
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAwareController
import tech.carcadex.kotlinbukkitkit.extensions.KListener
import tech.carcadex.kotlinbukkitkit.extensions.onlinePlayers
import tech.carcadex.kotlinbukkitkit.extensions.registerEvents
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent
import org.bukkit.event.inventory.InventoryType
import org.bukkit.event.player.PlayerPickupItemEvent
import org.bukkit.plugin.Plugin
internal object MenuControllerPlugins : PluginDisableAwareController<MenuController>() {
override val factory: (plugin: Plugin) -> MenuController = { MenuController(it) }
}
internal class MenuController(
override val plugin: Plugin,
) : KListener<Plugin>, PluginDisableAware {
init {
registerEvents(plugin)
}
override fun onDisable() {
onlinePlayers.forEach {
val menu = getMenuFromPlayer(it)
?.takeIf { it.plugin.name == plugin.name }
menu?.close(it, true)
}
}
@EventHandler(ignoreCancelled = true)
fun clickInventoryEvent(event: InventoryClickEvent) {
if (event.view.type == InventoryType.CHEST) {
val player = event.whoClicked as Player
val inv = event.inventory
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
if (menu != null) {
if (menu.plugin.name != plugin.name) return
if (event.rawSlot == event.slot) {
val clickedSlot = event.slot + 1
val slot = menu.slotOrBaseSlot(clickedSlot)
val interact = MenuPlayerSlotInteract(
menu, clickedSlot, slot,
player, inv, slot.cancel,
event.click, event.action,
event.currentItem, event.cursor,
event.hotbarButton,
)
slot.eventHandler.interact(interact)
if (interact.canceled) event.isCancelled = true
} else {
// TODO move item
event.isCancelled = true
}
}
}
}
@EventHandler(ignoreCancelled = true)
fun clickInteractEvent(event: InventoryDragEvent) {
if (event.view.type == InventoryType.CHEST) {
val player = event.whoClicked as Player
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
if (menu != null) {
if (menu.plugin.name != plugin.name) return
val pass = event.inventorySlots.firstOrNull { it in event.rawSlots }
if (pass != null) event.isCancelled = true
}
}
}
@EventHandler
fun onClose(event: InventoryCloseEvent) {
if (event.view.type == InventoryType.CHEST) {
val player = event.player as Player
val menu = getMenuFromPlayer(player)
if (menu != null) {
if (menu.plugin.name != plugin.name) return
menu.close(player, false)
}
}
}
@EventHandler(ignoreCancelled = true)
fun onPickupItemEvent(event: PlayerPickupItemEvent) {
val menu = getMenuFromPlayer(event.player)
if (menu != null) {
if (menu.plugin.name != plugin.name) return
event.isCancelled = true
}
}
}
@@ -0,0 +1,14 @@
package tech.carcadex.kotlinbukkitkit.menu
public interface MenuEventHandler {
public fun update(update: MenuPlayerUpdate)
public fun close(close: MenuPlayerClose)
public fun moveToMenu(moveToMenu: MenuPlayerMoveTo)
public fun preOpen(preOpen: MenuPlayerPreOpen)
public fun open(open: MenuPlayerOpen)
}
@@ -0,0 +1,121 @@
package tech.carcadex.kotlinbukkitkit.menu
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import java.util.*
public interface MenuPlayer {
public val menu: Menu<*>
public val player: Player
public fun putPlayerData(key: String, value: Any) {
val map = menu.playerData[player]
if (map != null) {
map[key] = value
} else menu.playerData[player] = WeakHashMap<String, Any>().apply { put(key, value) }
}
public fun getPlayerData(key: String): Any? = menu.playerData.get(player)?.get(key)
}
public interface MenuPlayerInventory : MenuPlayer {
public val inventory: Inventory
public fun close(): Unit = player.closeInventory()
public fun getItem(line: Int, slot: Int): ItemStack? {
return getItem(calculateSlot(line, slot))
}
public fun getItem(slot: Int): ItemStack? = inventory.getItem(slot - 1)
public fun setItem(line: Int, slot: Int, item: ItemStack?) {
setItem(calculateSlot(line, slot), item)
}
public fun setItem(slot: Int, item: ItemStack?) {
inventory.setItem(rawSlot(slot), item)
}
public fun getPlayerItem(line: Int, slot: Int): ItemStack? {
return getPlayerItem(calculateSlot(line, slot))
}
public fun getPlayerItem(slot: Int): ItemStack? = player.inventory.getItem(rawSlot(slot))
public fun setPlayerItem(line: Int, slot: Int, item: ItemStack?) {
setPlayerItem(calculateSlot(line, slot), item)
}
public fun setPlayerItem(slot: Int, item: ItemStack?) {
player.inventory.setItem(rawSlot(slot), item)
}
private fun getLineRange(line: Int) = calculateSlot(line, 1)..calculateEndLine(line)
public fun getLine(line: Int): List<ItemStack?> = getLineRange(line).map { getItem(it) }
public fun getPlayerLine(line: Int): List<ItemStack?> = getLineRange(line).map { getPlayerItem(it) }
public fun updateToPlayer() {
menu.update(player)
}
}
public interface MenuPlayerCancellable : MenuPlayer {
public var canceled: Boolean
}
public open class MenuPlayerInteract(
override val menu: Menu<*>,
override val player: Player,
override val inventory: Inventory,
override var canceled: Boolean,
) : MenuPlayerInventory, MenuPlayerCancellable
public class MenuPlayerPreOpen(
override val menu: Menu<*>,
override val player: Player,
override var canceled: Boolean = false,
) : MenuPlayerCancellable
public class MenuPlayerOpen(
override val menu: Menu<*>,
override val player: Player,
override val inventory: Inventory,
) : MenuPlayerInventory
public class MenuPlayerUpdate(
override val menu: Menu<*>,
override val player: Player,
override val inventory: Inventory,
public var title: String,
) : MenuPlayerInventory
public class MenuPlayerClose(
override val menu: Menu<*>,
override val player: Player,
) : MenuPlayer
public interface MenuPlayerMove : MenuPlayerInventory, MenuPlayerCancellable {
public val toMoveSlot: Int
public val toMoveItem: ItemStack?
}
public class MenuPlayerMoveTo(
menu: Menu<*>,
player: Player,
inventory: Inventory,
canceled: Boolean,
override val toMoveSlot: Int,
override val toMoveItem: ItemStack?,
public var targetSlot: Int,
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerMove {
public var targetCurrentItem: ItemStack?
set(value) {
setItem(targetSlot, value)
}
get() = getItem(targetSlot)
}
@@ -0,0 +1,29 @@
package tech.carcadex.kotlinbukkitkit.menu
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
public fun calculateSlot(line: Int, slot: Int): Int = calculateStartLine(line) + slot
public fun calculateStartLine(line: Int): Int = calculateEndLine(line) - 9
public fun calculateEndLine(line: Int): Int = line * 9
public fun rawSlot(slot: Int): Int = slot - 1
public fun Menu<*>.slotOrBaseSlot(slot: Int): Slot = slots[slot] ?: baseSlot
public fun Menu<*>.rangeOfSlots(): IntRange = 1..calculateEndLine(lines)
public fun Menu<*>.viewersFromPlayers(players: Set<Player>): Map<Player, Inventory> = viewers.filterKeys { it in players }
public fun Menu<*>.slotsWithBaseSlot(): List<Slot> = slots.values + baseSlot
public fun Menu<*>.hasPlayer(player: Player): Boolean = viewers.containsKey(player)
public fun Menu<*>.takeIfHasPlayer(player: Player): Menu<*>? = if (hasPlayer(player)) this else null
public fun inventoryIsMenu(inventory: Inventory): Boolean = inventory.holder is Menu<*>
public fun getMenuFromInventory(inventory: Inventory): Menu<*>? = inventory.holder as? Menu<*>
public fun getMenuFromPlayer(player: Player): Menu<*>? {
return getMenuFromInventory(player.openInventory.topInventory)?.takeIfHasPlayer(player)
}
@@ -0,0 +1,8 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl
import org.bukkit.entity.Player
import java.util.*
public fun MenuDSL.putPlayerData(player: Player, key: String, value: Any): Any? = playerData.getOrPut(player, { WeakHashMap() }).put(key, value)
public fun MenuDSL.getPlayerData(player: Player, key: String): Any? = playerData.get(player)?.get(key)
@@ -0,0 +1,46 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl
import tech.carcadex.kotlinbukkitkit.menu.Menu
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import org.bukkit.inventory.ItemStack
public inline fun MenuDSL.slot(
line: Int,
slot: Int,
item: ItemStack?,
block: SlotDSL.() -> Unit = {},
): SlotDSL = slot(calculateSlot(line, slot), item, block)
public inline fun MenuDSL.slot(
slot: Int,
item: ItemStack?,
block: SlotDSL.() -> Unit = {},
): SlotDSL = baseSlot.clone(item).apply(block).also {
setSlot(slot, it)
}
public interface MenuDSL : Menu<SlotDSL> {
override val eventHandler: MenuEventHandlerDSL
public fun onUpdate(update: MenuPlayerUpdateEvent) {
eventHandler.updateCallbacks.add(update)
}
public fun onClose(close: MenuPlayerCloseEvent) {
eventHandler.closeCallbacks.add(close)
}
public fun onMoveToMenu(moveToMenu: MenuPlayerMoveToEvent) {
eventHandler.moveToMenuCallbacks.add(moveToMenu)
}
public fun preOpen(preOpen: MenuPlayerPreOpenEvent) {
eventHandler.preOpenCallbacks.add(preOpen)
}
public fun onOpen(open: MenuPlayerOpenEvent) {
eventHandler.openCallbacks.add(open)
}
}
@@ -0,0 +1,199 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
import tech.carcadex.kotlinbukkitkit.extensions.task
import tech.carcadex.kotlinbukkitkit.menu.MenuControllerPlugins
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSLImpl
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotEventHandlerDSL
import tech.carcadex.kotlinbukkitkit.menu.rangeOfSlots
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
import tech.carcadex.kotlinbukkitkit.menu.slotOrBaseSlot
import tech.carcadex.kotlinbukkitkit.menu.viewersFromPlayers
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
import java.util.*
public inline fun WithPlugin<*>.menu(
displayName: String,
lines: Int,
cancelOnClick: Boolean = true,
block: MenuDSL.() -> Unit,
): MenuDSL = plugin.menu(displayName, lines, cancelOnClick, block)
public inline fun Plugin.menu(
displayName: String,
lines: Int,
cancelOnClick: Boolean = true,
block: MenuDSL.() -> Unit,
): MenuDSL = menu(displayName, lines, this, cancelOnClick, block)
public inline fun menu(
displayName: String,
lines: Int,
plugin: Plugin,
cancelOnClick: Boolean = true,
block: MenuDSL.() -> Unit,
): MenuDSL = MenuDSLImpl(plugin, displayName, lines, cancelOnClick).apply(block)
public class MenuDSLImpl(
public override val plugin: Plugin,
override var title: String,
override var lines: Int,
override var cancelOnClick: Boolean,
) : MenuDSL {
init {
MenuControllerPlugins.ensureInitialized(plugin)
}
private var task: BukkitTask? = null
override var updateDelay: Long = 0
set(value) {
field = value
removeTask()
if (value > 0 && viewers.isNotEmpty()) {
setNewTask()
}
}
override val viewers: MutableMap<Player, Inventory> = WeakHashMap()
override val slots: TreeMap<Int, SlotDSL> = TreeMap()
override val data: WeakHashMap<String, Any> = WeakHashMap<String, Any>()
override val playerData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap<Player, WeakHashMap<String, Any>>()
override val eventHandler: MenuEventHandlerDSL = MenuEventHandlerDSL(this)
override var baseSlot: SlotDSL = SlotDSLImpl(null, cancelOnClick, SlotEventHandlerDSL())
override fun setSlot(slot: Int, slotObj: SlotDSL) {
slots.put(slot, slotObj)
}
override fun update(players: Set<Player>) {
val viewers = viewersFromPlayers(players)
for ((player, inventory) in viewers) {
val update = MenuPlayerUpdate(this, player, inventory, title)
eventHandler.update(update)
// TODO title
/*if(update.title != title)*/
for (i in rangeOfSlots()) {
val slot = slotOrBaseSlot(i)
updateSlotOnlyPos(i, slot, player, inventory)
}
}
}
override fun updateSlot(slot: Slot, players: Set<Player>) {
val slots: Map<Int, SlotDSL> = if (slot === baseSlot) {
rangeOfSlots().mapNotNull { if (slots[it] == null) it to slot else null }.toMap()
} else {
rangeOfSlots().mapNotNull { if (slot === slots[it]) it to slot else null }.toMap()
}
for ((player, inventory) in viewersFromPlayers(players)) {
for ((pos, s) in slots) {
updateSlotOnlyPos(pos, s, player, inventory)
}
}
}
private fun updateSlotOnlyPos(pos: Int, slot: Slot, player: Player, inventory: Inventory) {
val slotUpdate = MenuPlayerSlotUpdate(this, pos, slot, player, inventory)
slot.eventHandler.update(slotUpdate)
}
override fun openToPlayer(vararg players: Player) {
for (player in players) {
close(player, false)
try {
val inv = inventory
viewers.put(player, inv)
val preOpen = MenuPlayerPreOpen(this, player)
eventHandler.preOpen(preOpen)
if (preOpen.canceled) return
for (i in rangeOfSlots()) {
val slot = slotOrBaseSlot(i)
val render = MenuPlayerSlotRender(this, i, slot, player, inv)
slot.eventHandler.render(render)
}
player.openInventory(inv)
val open = MenuPlayerOpen(this, player, inventory)
eventHandler.open(open)
if (task == null && updateDelay > 0 && viewers.isNotEmpty()) {
setNewTask()
}
} catch (e: Throwable) {
e.printStackTrace()
removePlayer(player, true)
}
}
}
override fun getInventory(): Inventory {
val slots = rangeOfSlots()
val inventory = Bukkit.createInventory(this, slots.endInclusive, title)
for (i in slots) {
val slot = slotOrBaseSlot(i)
val item = slot.item?.clone()
inventory.setItem(i - 1, item)
}
return inventory
}
private fun removePlayer(player: Player, closeInventory: Boolean): Boolean {
if (closeInventory) player.closeInventory()
val viewing = viewers.remove(player) != null
if (viewing) {
clearPlayerData(player)
}
return viewing
}
override fun close(player: Player, closeInventory: Boolean) {
if (removePlayer(player, closeInventory)) {
val menuClose = MenuPlayerClose(this, player)
eventHandler.close(menuClose)
if (task != null && updateDelay > 0 && viewers.isEmpty()) {
removeTask()
}
}
}
private fun setNewTask() {
task = task(repeatDelay = updateDelay) { update() }
}
private fun removeTask() {
task?.cancel()
task = null
}
}
@@ -0,0 +1,54 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl
import tech.carcadex.kotlinbukkitkit.menu.MenuEventHandler
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMoveTo
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
public typealias MenuPlayerUpdateEvent = MenuPlayerUpdate.() -> Unit
public typealias MenuPlayerCloseEvent = MenuPlayerClose.() -> Unit
public typealias MenuPlayerMoveToEvent = MenuPlayerMoveTo.() -> Unit
public typealias MenuPlayerPreOpenEvent = MenuPlayerPreOpen.() -> Unit
public typealias MenuPlayerOpenEvent = MenuPlayerOpen.() -> Unit
public class MenuEventHandlerDSL(public val menu: MenuDSL) : MenuEventHandler {
internal val updateCallbacks: MutableList<MenuPlayerUpdateEvent> = mutableListOf()
internal val closeCallbacks: MutableList<MenuPlayerCloseEvent> = mutableListOf()
internal val moveToMenuCallbacks: MutableList<MenuPlayerMoveToEvent> = mutableListOf()
internal val preOpenCallbacks: MutableList<MenuPlayerPreOpenEvent> = mutableListOf()
internal val openCallbacks: MutableList<MenuPlayerOpenEvent> = mutableListOf()
override fun update(update: MenuPlayerUpdate) {
for (callback in updateCallbacks) {
callback(update)
}
}
override fun close(close: MenuPlayerClose) {
for (callback in closeCallbacks) {
callback(close)
}
}
override fun moveToMenu(moveToMenu: MenuPlayerMoveTo) {
for (callback in moveToMenuCallbacks) {
callback(moveToMenu)
}
}
override fun preOpen(preOpen: MenuPlayerPreOpen) {
for (callback in preOpenCallbacks) {
callback(preOpen)
}
}
override fun open(open: MenuPlayerOpen) {
for (callback in openCallbacks) {
callback(open)
}
}
}
@@ -0,0 +1,134 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import tech.carcadex.kotlinbukkitkit.utility.collections.ObservableCollection
import org.bukkit.entity.Player
import java.lang.IllegalArgumentException
import java.util.TreeMap
import java.util.WeakHashMap
public typealias ItemsProvider<T> = () -> ObservableCollection<T>
public typealias ItemsAdapter<T> = MenuPlayer.(List<T>) -> List<T>
public enum class Orientation { HORIZONTAL, VERTICAL }
public inline fun <T> MenuPagination<T>.slot(
builder: PaginationSlotDSL<T>.() -> Unit,
) {
for (paginationSlot in paginationSlots.values) {
paginationSlot.builder()
}
}
public fun MenuDSL.setPlayerOpenPage(player: Player, page: Int) {
playerData.put(
player,
WeakHashMap<String, Any>().apply { put(PAGINATION_OPEN_PAGE_KEY, page) },
)
}
public inline fun <T> MenuDSL.pagination(
itemsProvider: ObservableCollection<T>,
nextPageSlot: SlotDSL,
previousPageSlot: SlotDSL,
autoUpdateSwitchPageSlot: Boolean = true,
startLine: Int = 1,
endLine: Int = lines - 1,
startSlot: Int = 1,
endSlot: Int = 9,
orientation: Orientation = Orientation.HORIZONTAL,
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
builder: MenuPaginationImpl<T>.() -> Unit,
): MenuPaginationImpl<T> {
return pagination(
{ itemsProvider },
nextPageSlot,
previousPageSlot,
autoUpdateSwitchPageSlot,
startLine,
endLine,
startSlot,
endSlot,
orientation,
itemsAdapterOnOpen,
itemsAdapterOnUpdate,
builder,
)
}
public inline fun <T> MenuDSL.pagination(
noinline itemsProvider: ItemsProvider<T>,
nextPageSlot: SlotDSL,
previousPageSlot: SlotDSL,
autoUpdateSwitchPageSlot: Boolean = true,
startLine: Int = 1,
endLine: Int = lines - 1,
startSlot: Int = 1,
endSlot: Int = 9,
orientation: Orientation = Orientation.HORIZONTAL,
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
builder: MenuPaginationImpl<T>.() -> Unit,
): MenuPaginationImpl<T> {
if (startSlot > endSlot) throw IllegalArgumentException()
if (startLine > endLine) throw IllegalArgumentException()
return MenuPaginationImpl(
this,
itemsProvider,
nextPageSlot,
previousPageSlot,
autoUpdateSwitchPageSlot,
startLine,
endLine,
startSlot,
endSlot,
orientation,
itemsAdapterOnOpen,
itemsAdapterOnUpdate,
).apply(builder)
}
public interface MenuPagination<T> {
public val menu: MenuDSL
public val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>>
public val paginationEventHandler: PaginationEventHandler
public val itemsProvider: ItemsProvider<T>
public val nextPageSlot: SlotDSL
public val previousPageSlot: SlotDSL
public val autoUpdateSwitchPageSlot: Boolean
public val startLine: Int
public val endLine: Int
public val startSlot: Int
public val endSlot: Int
public val orientation: Orientation
public val itemsAdapterOnOpen: ItemsAdapter<T>?
public val itemsAdapterOnUpdate: ItemsAdapter<T>?
public fun onPageChange(pageChange: MenuPlayerPageChangeEvent) {
paginationEventHandler.pageChangeCallbacks.add(pageChange)
}
public fun onPageAvailable(pageAvailable: MenuPlayerPageAvailableEvent) {
paginationEventHandler.pageAvailableCallbacks.add(pageAvailable)
}
public fun hasPreviousPage(player: Player): Boolean
public fun hasNextPage(player: Player): Boolean
public fun getPlayerCurrentPage(player: Player): Int
public fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory)
}
@@ -0,0 +1,265 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSLImpl
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import org.bukkit.entity.Player
import java.util.*
internal const val PAGINATION_OPEN_PAGE_KEY = "PAGINATION:open_page"
public class MenuPaginationImpl<T>(
override val menu: MenuDSL,
override val itemsProvider: ItemsProvider<T>,
override val nextPageSlot: SlotDSL,
override val previousPageSlot: SlotDSL,
override val autoUpdateSwitchPageSlot: Boolean,
override val startLine: Int = 1,
override val endLine: Int = menu.lines - 1,
override val startSlot: Int = 1,
override val endSlot: Int = 9,
override val orientation: Orientation = Orientation.HORIZONTAL,
override val itemsAdapterOnOpen: ItemsAdapter<T>? = null,
override val itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
) : MenuPagination<T> {
override val paginationEventHandler: PaginationEventHandler = PaginationEventHandler()
override val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>> = TreeMap()
internal val currentPlayerPage = WeakHashMap<Player, Int>()
// should be null when is not mapped (for not allocate a new list everytime)
internal val currentPlayerItems = WeakHashMap<Player, List<T>>()
internal val itemSlotData = WeakHashMap<T, WeakHashMap<String, Any>>()
internal val itemPlayerSlotData = WeakHashMap<T, WeakHashMap<Player, WeakHashMap<String, Any>>>()
init {
// setup next page button click
nextPageSlot.onClick {
if (hasNextPage(player)) {
nextPage(this)
}
}
// setup previous page button click
previousPageSlot.onClick {
if (hasPreviousPage(player)) {
previousPage(this)
}
}
menu.preOpen {
// adapting items for player
val items = itemsAdapterOnOpen?.let { it(getPlayerItems(player).toList()) }
if (items != null) {
currentPlayerItems[player] = items
}
// set the menu page if player informed in [MenuDSL.setPlayerOpenPage]
val openPage = menu.playerData.get(player)?.get(PAGINATION_OPEN_PAGE_KEY) as? Int?
if (openPage != null) {
// check if openPage is valid or cancel and trigger a event
if (isPageAvailable(player, openPage)) {
currentPlayerPage[player] = openPage
} else {
canceled = true
paginationEventHandler.pageAvailable(this)
}
}
}
// cleaning player data on menu close
menu.onClose {
currentPlayerPage.remove(player)
currentPlayerItems.remove(player)
for (value in itemPlayerSlotData.values) {
value.remove(player)
}
}
// if enabled, update next and previous slots when page change
if (autoUpdateSwitchPageSlot) {
onPageChange {
menu.updateSlot(nextPageSlot, player)
menu.updateSlot(previousPageSlot, player)
}
}
// setup pagination slots
for (line in startLine..endLine) {
for (slotPos in startSlot..endSlot) {
val menuSlot = calculateSlot(line, slotPos)
val slotRoot = menu.slot(menuSlot, null)
val paginationSlot = PaginationSlotDSLImpl(
this,
slotRoot,
)
slotRoot.apply {
fun getCurrentItemForPlayer(player: Player): T? {
return getCurrentItemForPlayer(
menuSlot,
getPlayerCurrentPage(player),
player,
)
}
onRender {
paginationSlot.paginationEventHandler.handleRender(
getCurrentItemForPlayer(player),
this,
)
}
onUpdate {
paginationSlot.paginationEventHandler.handleUpdate(
getCurrentItemForPlayer(player),
this,
)
}
onClick {
paginationSlot.paginationEventHandler.handleInteract(
getCurrentItemForPlayer(player),
this,
)
}
}
paginationSlots.put(menuSlot, paginationSlot)
}
}
}
override fun hasPreviousPage(player: Player): Boolean {
return isPageAvailable(player, getPlayerCurrentPage(player) - 1)
}
override fun hasNextPage(player: Player): Boolean {
return isPageAvailable(player, getPlayerCurrentPage(player) + 1)
}
override fun getPlayerCurrentPage(player: Player): Int = currentPlayerPage.getOrDefault(player, 1)
/**
* Update current item list calling itemsUpdateFilter
*/
override fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory) {
val player = menuPlayer.player
// checking if adapter is not null, other wise, ignore
if (itemsAdapterOnUpdate != null) {
val items = itemsAdapterOnUpdate.let { menuPlayer.it(itemsProvider().toList()) }
currentPlayerItems[player] = items
// setting player to the last page if he is not in a page with items after adapting
if (!isPageAvailable(player, getPlayerCurrentPage(player))) {
currentPlayerPage[player] = maxPages(player)
}
// calling update slot to the PaginationSlotDSL
forEachSlot { slotPos, pageSlot ->
val currentPage = getPlayerCurrentPage(player)
val item: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
pageSlot.updateSlot(item, item, slotPos, menuPlayer)
}
// calling event because this remove items causing no more page needed
paginationEventHandler.pageChange(menuPlayer)
}
}
internal fun getCurrentItemForPlayer(slotPos: Int, page: Int, player: Player): T? {
val items = getPlayerItems(player)
val startLineNotUsage = startLine - 1
val lineEndSlotNotUsage = 9 - endSlot
val startSlotNotUsage = startSlot - 1
val startLineSlotsUsage = startLineNotUsage * 9
val currentLine = (slotPos / 9) + 1
val currentLineUsage = currentLine - startLineNotUsage
val currentUsageStartSlots = startSlotNotUsage * currentLineUsage
val currentEndSlotUsage = if (currentLineUsage > 1) lineEndSlotNotUsage * (currentLineUsage - 1) else 0
val slotItemIndex = pageStartIndex(page) + (slotPos - startLineSlotsUsage - currentUsageStartSlots - currentEndSlotUsage)
return items.asSequence().elementAtOrNull(slotItemIndex - 1)
}
internal fun nextPage(menuPlayerInventory: MenuPlayerInventory) {
val nextPage = getPlayerCurrentPage(menuPlayerInventory.player) + 1
changePage(menuPlayerInventory, nextPage)
}
internal fun previousPage(menuPlayerInventory: MenuPlayerInventory) {
val previousPage = getPlayerCurrentPage(menuPlayerInventory.player) - 1
changePage(menuPlayerInventory, previousPage)
}
private fun changePage(menuPlayerInventory: MenuPlayerInventory, nextPage: Int) {
val player = menuPlayerInventory.player
val currentPage = getPlayerCurrentPage(player)
currentPlayerPage[player] = nextPage
forEachSlot { slotPos, pageSlot ->
val actualItem: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
val nextItem: T? = getCurrentItemForPlayer(slotPos, nextPage, player)
pageSlot.updateSlot(actualItem, nextItem, slotPos, menuPlayerInventory, true)
paginationEventHandler.pageChange(menuPlayerInventory)
}
}
private inline fun forEachSlot(
block: (slotPos: Int, pageSlot: PaginationSlotDSLImpl<T>) -> Unit,
) {
for (line in startLine..endLine) {
for (slot in startSlot..endSlot) {
val slotPos = calculateSlot(line, slot)
val pageSlot = paginationSlots[slotPos] as? PaginationSlotDSLImpl<T>
?: continue
block(slotPos, pageSlot)
}
}
}
private fun pageStartIndex(page: Int) = ((page - 1) * maxSlotPerPage())
private fun getPlayerItems(player: Player) = currentPlayerItems[player] ?: itemsProvider()
private fun maxSlotPerPage() = (endLine - startLine + 1) * (endSlot - startSlot + 1)
private fun maxPages(player: Player): Int {
val itemsCount = getPlayerItems(player).size
val maxPerPage = maxSlotPerPage()
var pages = (itemsCount / maxPerPage).toInt()
val mod = itemsCount % maxPerPage
if (mod > 0) pages++
return pages
}
private fun isPageAvailable(player: Player, page: Int): Boolean {
val maxPages = maxPages(player)
return page in 1..maxPages
}
}
@@ -0,0 +1,23 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
public typealias MenuPlayerPageChangeEvent = MenuPlayer.() -> Unit
public typealias MenuPlayerPageAvailableEvent = MenuPlayer.() -> Unit
public class PaginationEventHandler {
internal val pageChangeCallbacks: MutableList<MenuPlayerPageChangeEvent> = mutableListOf()
internal val pageAvailableCallbacks: MutableList<MenuPlayerPageAvailableEvent> = mutableListOf()
public fun pageChange(pageChange: MenuPlayer) {
for (callback in pageChangeCallbacks) {
callback(pageChange)
}
}
public fun pageAvailable(pageAvailable: MenuPlayer) {
for (callback in pageAvailableCallbacks) {
callback(pageAvailable)
}
}
}
@@ -0,0 +1,35 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import org.bukkit.entity.Player
import java.util.*
public interface PaginationSlotDSL<T> {
public val slotRoot: SlotDSL
public val paginationEventHandler: PaginationSlotEventHandler<T>
public val slotData: WeakHashMap<String, Any>
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
/**
* Cancel the interaction with this slot
*/
public var cancel: Boolean
public fun onPageChange(pageChange: MenuPlayerSlotPageChangeEvent<T>) {
paginationEventHandler.pageChangeCallbacks.add(pageChange)
}
public fun onClick(click: MenuPlayerPageSlotInteractEvent<T>) {
paginationEventHandler.interactCallbacks.add(click)
}
public fun onRender(render: MenuPlayerPageSlotRenderEvent<T>) {
paginationEventHandler.renderCallbacks.add(render)
}
public fun onUpdate(update: MenuPlayerPageSlotUpdateEvent<T>) {
paginationEventHandler.updateCallbacks.add(update)
}
}
@@ -0,0 +1,94 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.MenuPaginationImpl
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
import org.bukkit.entity.Player
import java.util.*
public class PaginationSlotDSLImpl<T>(
private val pagination: MenuPaginationImpl<T>,
override val slotRoot: SlotDSL,
) : PaginationSlotDSL<T> {
override val paginationEventHandler: PaginationSlotEventHandler<T> = PaginationSlotEventHandler<T>()
override var cancel: Boolean
get() = slotRoot.cancel
set(value) { slotRoot.cancel = value }
override val slotData: WeakHashMap<String, Any>
get() = slotRoot.slotData
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
get() = slotRoot.playerSlotData
internal fun updateSlot(
actualItem: T?,
nextItem: T?,
slotPos: Int,
menuPlayerInventory: MenuPlayerInventory,
isPageChange: Boolean = false,
) {
if (isPageChange) {
relocateSlotData(actualItem, nextItem)
// triggering event
paginationEventHandler.handlePageChange(
actualItem,
MenuPlayerSlotPageChange(
pagination.menu,
slotPos,
slotRoot,
menuPlayerInventory.player,
menuPlayerInventory.inventory,
),
)
}
// cleaning item in the inventory slot
menuPlayerInventory.setItem(slotPos, null)
paginationEventHandler.handleRender(
nextItem,
MenuPlayerSlotRender(
pagination.menu,
slotPos,
slotRoot,
menuPlayerInventory.player,
menuPlayerInventory.inventory,
),
)
}
internal fun relocateSlotData(actualItem: T?, nextItem: T?) {
if (actualItem != null) {
// caching the current Data from Slot
val slotData = WeakHashMap(slotData)
val playerSlotData = WeakHashMap(playerSlotData)
if (slotData.isNotEmpty()) {
pagination.itemSlotData[actualItem] = slotData
}
if (playerSlotData.isNotEmpty()) {
pagination.itemPlayerSlotData[actualItem] = playerSlotData
}
}
// cleaning current data in the Slot
slotData.clear()
playerSlotData.clear()
if (nextItem != null) {
val nextSlotData = pagination.itemSlotData[nextItem]
val nextPlayerSlotData = pagination.itemPlayerSlotData[nextItem]
if (nextSlotData != null) {
slotData.putAll(nextSlotData)
}
if (nextPlayerSlotData != null) {
playerSlotData.putAll(nextPlayerSlotData)
}
}
}
}
@@ -0,0 +1,54 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
import tech.carcadex.kotlinbukkitkit.menu.Menu
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerInventorySlot
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
public typealias MenuPlayerSlotPageChangeEvent<T> = MenuPlayerSlotPageChange.(T?) -> Unit
public typealias MenuPlayerPageSlotInteractEvent<T> = MenuPlayerSlotInteract.(T?) -> Unit
public typealias MenuPlayerPageSlotRenderEvent<T> = MenuPlayerSlotRender.(T?) -> Unit
public typealias MenuPlayerPageSlotUpdateEvent<T> = MenuPlayerSlotUpdate.(T?) -> Unit
public class PaginationSlotEventHandler<T> {
internal val pageChangeCallbacks = mutableListOf<MenuPlayerSlotPageChangeEvent<T>>()
internal val interactCallbacks = mutableListOf<MenuPlayerPageSlotInteractEvent<T>>()
internal val renderCallbacks = mutableListOf<MenuPlayerPageSlotRenderEvent<T>>()
internal val updateCallbacks = mutableListOf<MenuPlayerPageSlotUpdateEvent<T>>()
public fun handlePageChange(currentItem: T?, pageChange: MenuPlayerSlotPageChange) {
for (callback in pageChangeCallbacks) {
callback(pageChange, currentItem)
}
}
public fun handleRender(currentItem: T?, render: MenuPlayerSlotRender) {
for (callback in renderCallbacks) {
callback(render, currentItem)
}
}
public fun handleUpdate(currentItem: T?, update: MenuPlayerSlotUpdate) {
for (callback in updateCallbacks) {
callback(update, currentItem)
}
}
public fun handleInteract(currentItem: T?, interact: MenuPlayerSlotInteract) {
for (callback in interactCallbacks) {
callback(interact, currentItem)
}
}
}
public class MenuPlayerSlotPageChange(
override val menu: Menu<*>,
override val slotPos: Int,
override val slot: Slot,
override val player: Player,
override val inventory: Inventory,
) : MenuPlayerInventorySlot
@@ -0,0 +1,27 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
import org.bukkit.inventory.ItemStack
public interface SlotDSL : Slot {
override val eventHandler: SlotEventHandlerDSL
public fun onClick(click: MenuPlayerSlotInteractEvent) {
eventHandler.interactCallbacks.add(click)
}
public fun onRender(render: MenuPlayerSlotRenderEvent) {
eventHandler.renderCallbacks.add(render)
}
public fun onUpdate(update: MenuPlayerSlotUpdateEvent) {
eventHandler.updateCallbacks.add(update)
}
public fun onMoveToSlot(moveToSlot: MenuPlayerSlotMoveToEvent) {
eventHandler.moveToSlotCallbacks.add(moveToSlot)
}
override fun clone(item: ItemStack?): SlotDSL
}
@@ -0,0 +1,22 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.*
public fun MenuDSL.newSlot(item: ItemStack?, builder: SlotDSL.() -> Unit): SlotDSL {
return SlotDSLImpl(item, cancelOnClick).apply(builder)
}
public class SlotDSLImpl(
override val item: ItemStack?,
override var cancel: Boolean,
override val eventHandler: SlotEventHandlerDSL = SlotEventHandlerDSL(),
) : SlotDSL {
override val slotData: WeakHashMap<String, Any> = WeakHashMap()
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap()
override fun clone(item: ItemStack?): SlotDSL = SlotDSLImpl(item, cancel, eventHandler.clone())
}
@@ -0,0 +1,53 @@
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotMoveTo
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
import tech.carcadex.kotlinbukkitkit.menu.slot.SlotEventHandler
public typealias MenuPlayerSlotInteractEvent = MenuPlayerSlotInteract.() -> Unit
public typealias MenuPlayerSlotRenderEvent = MenuPlayerSlotRender.() -> Unit
public typealias MenuPlayerSlotUpdateEvent = MenuPlayerSlotUpdate.() -> Unit
public typealias MenuPlayerSlotMoveToEvent = MenuPlayerSlotMoveTo.() -> Unit
public class SlotEventHandlerDSL : SlotEventHandler {
internal val interactCallbacks = mutableListOf<MenuPlayerSlotInteractEvent>()
internal val renderCallbacks = mutableListOf<MenuPlayerSlotRenderEvent>()
internal val updateCallbacks = mutableListOf<MenuPlayerSlotUpdateEvent>()
internal val moveToSlotCallbacks = mutableListOf<MenuPlayerSlotMoveToEvent>()
override fun interact(interact: MenuPlayerSlotInteract) {
for (callback in interactCallbacks) {
callback(interact)
}
}
override fun render(render: MenuPlayerSlotRender) {
for (callback in renderCallbacks) {
callback(render)
}
}
override fun update(update: MenuPlayerSlotUpdate) {
for (callback in updateCallbacks) {
callback(update)
}
}
override fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo) {
for (callback in moveToSlotCallbacks) {
callback(moveToSlot)
}
}
override fun clone(): SlotEventHandlerDSL {
return SlotEventHandlerDSL().also {
it.interactCallbacks.addAll(interactCallbacks)
it.renderCallbacks.addAll(renderCallbacks)
it.updateCallbacks.addAll(updateCallbacks)
it.moveToSlotCallbacks.addAll(moveToSlotCallbacks)
}
}
}
@@ -0,0 +1,33 @@
package tech.carcadex.kotlinbukkitkit.menu.slot
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.*
public interface Slot {
public val item: ItemStack?
public val eventHandler: SlotEventHandler
public val slotData: WeakHashMap<String, Any>
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
/**
* Cancel the interaction with this slot
*/
public var cancel: Boolean
/**
* a clone of Slot without slotData and playerSlotData
*/
public fun clone(item: ItemStack?): Slot
public fun clearSlotData() {
slotData.clear()
}
public fun clearPlayerData(player: Player) {
playerSlotData.remove(player)
}
}
@@ -0,0 +1,14 @@
package tech.carcadex.kotlinbukkitkit.menu.slot
public interface SlotEventHandler {
public fun interact(interact: MenuPlayerSlotInteract)
public fun render(render: MenuPlayerSlotRender)
public fun update(update: MenuPlayerSlotUpdate)
public fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo)
public fun clone(): SlotEventHandler
}
@@ -0,0 +1,85 @@
package tech.carcadex.kotlinbukkitkit.menu.slot
import tech.carcadex.kotlinbukkitkit.menu.Menu
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInteract
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMove
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryAction
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import java.util.WeakHashMap
public val MenuPlayerSlot.rawSlotPos: Int get() = slotPos - 1
public interface MenuPlayerSlot : MenuPlayer {
public val slotPos: Int
public val slot: Slot
public fun putPlayerSlotData(key: String, value: Any) {
slot.playerSlotData.getOrPut(player) {
WeakHashMap()
}[key] = value
}
public fun getPlayerSlotData(key: String): Any? = slot.playerSlotData.get(player)?.get(key)
}
public interface MenuPlayerInventorySlot : MenuPlayerSlot, MenuPlayerInventory {
public var showingItem: ItemStack?
get() = getItem(slotPos)?.takeUnless { it.type == Material.AIR }
set(value) = setItem(slotPos, value)
public fun updateSlotToPlayer() {
menu.updateSlot(slot, player)
}
public fun updateSlot() {
menu.updateSlot(slot)
}
}
public open class MenuPlayerSlotInteract(
menu: Menu<*>,
override val slotPos: Int,
override val slot: Slot,
player: Player,
inventory: Inventory,
canceled: Boolean,
public val click: ClickType,
public val action: InventoryAction,
public val clicked: ItemStack?,
public val cursor: ItemStack?,
public val hotbarKey: Int,
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerInventorySlot
public class MenuPlayerSlotMoveTo(
override val menu: Menu<*>,
override val slotPos: Int,
override val slot: Slot,
override val player: Player,
override val inventory: Inventory,
override var canceled: Boolean,
override val toMoveSlot: Int,
override var toMoveItem: ItemStack?,
) : MenuPlayerInventorySlot, MenuPlayerMove
public class MenuPlayerSlotRender(
override val menu: Menu<*>,
override val slotPos: Int,
override val slot: Slot,
override val player: Player,
override val inventory: Inventory,
) : MenuPlayerInventorySlot
public class MenuPlayerSlotUpdate(
override val menu: Menu<*>,
override val slotPos: Int,
override val slot: Slot,
override val player: Player,
override val inventory: Inventory,
) : MenuPlayerInventorySlot