Added GUIInstance structure; GUIIndividual now available

This commit is contained in:
bluefireoly
2020-10-25 10:54:52 +01:00
parent 9ff49ad624
commit 6db78d08f4
13 changed files with 192 additions and 153 deletions

View File

@@ -2,116 +2,81 @@
package net.axay.kspigot.gui package net.axay.kspigot.gui
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import java.util.*
import kotlin.collections.HashSet
// TODO unregister in all layers
private const val DEFAULT_PAGE = 1 private const val DEFAULT_PAGE = 1
class GUIData<T : ForInventory>( class GUIData<T : ForInventory>(
val guiType: GUIType<T>, val guiType: GUIType<T>,
val title: String?, val title: String?,
internal val pages: Map<Int, GUIPage<T>>, internal val pages: Map<Int, GUIPage<T>>,
val transitionTo: InventoryChangeEffect?, val transitionTo: InventoryChangeEffect?,
val transitionFrom: InventoryChangeEffect?, val transitionFrom: InventoryChangeEffect?,
internal val generalOnClick: ((GUIClickEvent<T>) -> Unit)? internal val generalOnClick: ((GUIClickEvent<T>) -> Unit)?
) )
abstract class GUI<T : ForInventory>( abstract class GUI<T : ForInventory>(
val data: GUIData<T> val data: GUIData<T>
) { ) {
abstract fun getInstance(player: Player): GUIInstance<T>
}
var currentPageInt: Int = DEFAULT_PAGE; protected set class GUIShared<T : ForInventory>(
val currentPage guiData: GUIData<T>
get() = getPage(currentPageInt) ) : GUI<T>(guiData) {
?: throw IllegalStateException("The currentPageInt has no associated page!")
internal abstract val bukkitInventory: Inventory val singleInstance by lazy { GUIInstance(this, null).apply { register() } }
internal var isInMove: Boolean = false override fun getInstance(player: Player) = singleInstance
internal val currentElements = HashSet<GUIElement<*>>()
internal abstract fun loadPageUnsafe(
page: Int,
offsetHorizontally: Int = 0,
offsetVertically: Int = 0
)
internal abstract fun loadPageUnsafe(
page: GUIPage<*>,
offsetHorizontally: Int = 0,
offsetVertically: Int = 0
)
internal abstract fun loadContent(
content: Map<Int, GUISlot<*>>,
offsetHorizontally: Int = 0,
offsetVertically: Int = 0
)
/**
* @return True, if the [inventory] belongs to this GUI.
*/
abstract fun isThisInv(inventory: Inventory): Boolean
/**
* Registers this GUI.
* (KSpigot will listen for actions in the inventory.)
*/
@Suppress("UNCHECKED_CAST")
fun register() = GUIHolder.register(this as GUI<ForInventory>)
/**
* Stops KSpigot from listening to actions in this
* GUI anymore.
*/
@Suppress("UNCHECKED_CAST")
fun unregister() = GUIHolder.unregister(this as GUI<ForInventory>)
/**
* Loads the specified page in order to display it in the GUI.
*/
fun loadPage(page: GUIPage<T>) = loadPageUnsafe(page)
/**
* Temporarily sets the given item at the given slots.
*/
abstract operator fun set(slot: InventorySlotCompound<T>, value: ItemStack)
/**
* Searches for a page associated to the given [page] index.
*/
fun getPage(page: Int?) = data.pages[page]
/**
* Reloads the current page.
*/
fun reloadCurrentPage() {
if (!isInMove)
loadPage(currentPage)
}
} }
// Inventory GUI implementations class GUIIndividual<T : ForInventory>(
guiData: GUIData<T>,
val resetOnClose: Boolean
) : GUI<T>(guiData) {
class GUIShared<T : ForInventory>( private val playerInstances = HashMap<Player, GUIInstance<T>>()
GUIData: GUIData<T>
) : GUI<T>(GUIData) {
override val bukkitInventory = data.guiType.createBukkitInv(null, data.title) override fun getInstance(player: Player) =
playerInstances[player] ?: createInstance(player)
private fun createInstance(player: Player) =
GUIInstance(this, player).apply { playerInstances[player] = this }
}
class GUIInstance<T : ForInventory>(
val gui: GUI<T>,
holder: Player?
) {
internal val bukkitInventory = gui.data.guiType.createBukkitInv(holder, gui.data.title)
private val currentElements = HashSet<GUIElement<*>>()
internal var isInMove: Boolean = false
var currentPageInt: Int = DEFAULT_PAGE; private set
val currentPage
get() = getPage(currentPageInt)
?: throw IllegalStateException("The currentPageInt has no associated page!")
init { init {
loadPageUnsafe(DEFAULT_PAGE) loadPageUnsafe(DEFAULT_PAGE)
} }
override fun isThisInv(inventory: Inventory) = inventory == bukkitInventory internal fun loadPageUnsafe(page: Int, offsetHorizontally: Int = 0, offsetVertically: Int = 0) {
gui.data.pages[page]?.let { loadPageUnsafe(it, offsetHorizontally, offsetVertically) }
override fun loadPageUnsafe(page: Int, offsetHorizontally: Int, offsetVertically: Int) {
data.pages[page]?.let { loadPageUnsafe(it, offsetHorizontally, offsetVertically) }
} }
override fun loadPageUnsafe(page: GUIPage<*>, offsetHorizontally: Int, offsetVertically: Int) { internal fun loadPageUnsafe(page: GUIPage<*>, offsetHorizontally: Int = 0, offsetVertically: Int = 0) {
val ifOffset = offsetHorizontally != 0 || offsetVertically != 0 val ifOffset = offsetHorizontally != 0 || offsetVertically != 0
@@ -122,10 +87,12 @@ class GUIShared<T : ForInventory>(
currentElements.clear() currentElements.clear()
// register this inv for all new elements // register this inv for all new elements
HashSet(page.slots.values).forEach { if (it is GUIElement) { HashSet(page.slots.values).forEach {
currentElements += it if (it is GUIElement) {
it.startUsing(this) currentElements += it
} } it.startUsing(this)
}
}
currentPageInt = page.number currentPageInt = page.number
@@ -135,15 +102,15 @@ class GUIShared<T : ForInventory>(
} }
override fun loadContent( internal fun loadContent(
content: Map<Int, GUISlot<*>>, content: Map<Int, GUISlot<*>>,
offsetHorizontally: Int, offsetHorizontally: Int = 0,
offsetVertically: Int offsetVertically: Int = 0
) { ) {
val ifOffset = offsetHorizontally != 0 || offsetVertically != 0 val ifOffset = offsetHorizontally != 0 || offsetVertically != 0
val dimensions = data.guiType.dimensions val dimensions = gui.data.guiType.dimensions
// clear the space which will be redefined // clear the space which will be redefined
if (ifOffset) { if (ifOffset) {
@@ -173,10 +140,50 @@ class GUIShared<T : ForInventory>(
} }
override operator fun set(slot: InventorySlotCompound<T>, value: ItemStack) { /**
slot.realSlotsWithInvType(data.guiType).forEach { * Registers this GUI.
* (KSpigot will listen for actions in the inventory.)
*/
@Suppress("UNCHECKED_CAST")
fun register() = GUIHolder.register(this as GUIInstance<ForInventory>)
/**
* Stops KSpigot from listening to actions in this
* GUI anymore.
*/
@Suppress("UNCHECKED_CAST")
fun unregister() = GUIHolder.unregister(this as GUIInstance<ForInventory>)
/**
* @return True, if the [inventory] belongs to this GUI.
*/
fun isThisInv(inventory: Inventory) = inventory == bukkitInventory
/**
* Loads the specified page in order to display it in the GUI.
*/
fun loadPage(page: GUIPage<T>) = loadPageUnsafe(page)
/**
* Temporarily sets the given item at the given slots.
*/
operator fun set(slot: InventorySlotCompound<T>, value: ItemStack) {
slot.realSlotsWithInvType(gui.data.guiType).forEach {
bukkitInventory.setItem(it, value) bukkitInventory.setItem(it, value)
} }
} }
/**
* Searches for a page associated to the given [page] index.
*/
fun getPage(page: Int?) = gui.data.pages[page]
/**
* Reloads the current page.
*/
fun reloadCurrentPage() {
if (!isInMove)
loadPage(currentPage)
}
} }

View File

@@ -7,14 +7,14 @@ import org.bukkit.inventory.ItemStack
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
fun <T : ForInventory> kSpigotGUI( fun <T : ForInventory> kSpigotGUI(
type: GUIType<T>, type: GUIType<T>,
shared: Boolean = true, guiCreator: GUICreator<T>,
builder: GUIBuilder<T>.() -> Unit, builder: GUIBuilder<T>.() -> Unit,
) = GUIBuilder(type, shared).apply(builder).build() ) = GUIBuilder(type, guiCreator).apply(builder).build()
class GUIBuilder<T : ForInventory>( class GUIBuilder<T : ForInventory>(
val type: GUIType<T>, val type: GUIType<T>,
val shared: Boolean private val guiCreator: GUICreator<T>
) { ) {
var title: String = "" var title: String = ""
@@ -39,18 +39,15 @@ class GUIBuilder<T : ForInventory>(
onClickElement = onClick onClickElement = onClick
} }
internal fun build(): GUI<T> { internal fun build() = guiCreator.createInstance(
val guiData = GUIData(type, title, guiSlots, transitionTo, transitionFrom, onClickElement) GUIData(type, title, guiSlots, transitionTo, transitionFrom, onClickElement)
val gui = )
if (shared) GUIShared(guiData) else TODO("Currently, there is no non-shared GUI implementation available.")
return gui.apply { register() }
}
} }
class GUIPageBuilder<T : ForInventory>( class GUIPageBuilder<T : ForInventory>(
private val type: GUIType<T>, private val type: GUIType<T>,
val page: Int val page: Int
) { ) {
private val guiSlots = HashMap<Int, GUISlot<T>>() private val guiSlots = HashMap<Int, GUISlot<T>>()

View File

@@ -1,8 +1,10 @@
package net.axay.kspigot.gui package net.axay.kspigot.gui
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
class GUIClickEvent<T : ForInventory>( class GUIClickEvent<T : ForInventory>(
val bukkitEvent: InventoryClickEvent, val bukkitEvent: InventoryClickEvent,
val gui: GUI<T> val guiInstance: GUIInstance<T>,
val player: Player
) )

View File

@@ -0,0 +1,15 @@
package net.axay.kspigot.gui
abstract class GUICreator<T : ForInventory> {
abstract fun createInstance(guiData: GUIData<T>): GUI<T>
}
class SharedGUICreator<T : ForInventory> : GUICreator<T>() {
override fun createInstance(guiData: GUIData<T>) = GUIShared(guiData)
}
class IndividualGUICreator<T : ForInventory>(
private val resetOnClose: Boolean
) : GUICreator<T>() {
override fun createInstance(guiData: GUIData<T>) = GUIIndividual(guiData, resetOnClose)
}

View File

@@ -13,13 +13,13 @@ abstract class GUIElement<T : ForInventory> : GUISlot<T>() {
abstract fun getItemStack(slot: Int): ItemStack abstract fun getItemStack(slot: Int): ItemStack
final override fun onClick(clickEvent: GUIClickEvent<T>) { final override fun onClick(clickEvent: GUIClickEvent<T>) {
clickEvent.gui.data.generalOnClick?.invoke(clickEvent) clickEvent.guiInstance.gui.data.generalOnClick?.invoke(clickEvent)
onClickElement(clickEvent) onClickElement(clickEvent)
} }
protected abstract fun onClickElement(clickEvent: GUIClickEvent<T>) protected abstract fun onClickElement(clickEvent: GUIClickEvent<T>)
internal open fun startUsing(gui: GUI<*>) { } internal open fun startUsing(gui: GUIInstance<*>) {}
internal open fun stopUsing(gui: GUI<*>) { } internal open fun stopUsing(gui: GUIInstance<*>) {}
} }

View File

@@ -1,15 +1,18 @@
package net.axay.kspigot.gui package net.axay.kspigot.gui
import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player
import org.bukkit.inventory.InventoryView import org.bukkit.inventory.InventoryView
fun HumanEntity.openGUI(gui: GUI<*>, page: Int? = null): InventoryView? { fun Player.openGUI(gui: GUI<*>, page: Int? = null): InventoryView? {
closeInventory() closeInventory()
return openGUIInstance(gui.getInstance(this), page)
}
internal fun Player.openGUIInstance(guiInstance: GUIInstance<*>, page: Int? = null): InventoryView? {
if (page != null) if (page != null)
gui.loadPageUnsafe(page) guiInstance.loadPageUnsafe(page)
return openInventory(gui.bukkitInventory) return openInventory(guiInstance.bukkitInventory)
} }

View File

@@ -1,19 +1,20 @@
package net.axay.kspigot.gui package net.axay.kspigot.gui
import net.axay.kspigot.event.listen import net.axay.kspigot.event.listen
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryAction import org.bukkit.event.inventory.InventoryAction
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
object GUIHolder : AutoCloseable { object GUIHolder : AutoCloseable {
private val registered = HashSet<GUI<ForInventory>>() private val registered = HashSet<GUIInstance<ForInventory>>()
fun register(GUI: GUI<ForInventory>) { fun register(guiInstance: GUIInstance<ForInventory>) {
registered.add(GUI) registered.add(guiInstance)
} }
fun unregister(GUI: GUI<ForInventory>) { fun unregister(guiInstance: GUIInstance<ForInventory>) {
registered.remove(GUI) registered.remove(guiInstance)
} }
init { init {
@@ -24,13 +25,18 @@ object GUIHolder : AutoCloseable {
val inv = registered.find { search -> search.isThisInv(clickedInv) } ?: return@listen val inv = registered.find { search -> search.isThisInv(clickedInv) } ?: return@listen
val player = it.whoClicked as? Player ?: kotlin.run {
it.isCancelled = true
return@listen
}
if (inv.isInMove) { if (inv.isInMove) {
it.isCancelled = true it.isCancelled = true
return@listen return@listen
} }
if (it.action.isGUIClick) if (it.action.isGUIClick)
inv.currentPage.slots[it.slot]?.onClick(GUIClickEvent(it, inv)) ?: kotlin.run { inv.currentPage.slots[it.slot]?.onClick(GUIClickEvent(it, inv, player)) ?: kotlin.run {
it.isCancelled = true it.isCancelled = true
} }
else else

View File

@@ -36,7 +36,7 @@ enum class InventoryChangeEffect(
INSTANT(PageChangeEffect.INSTANT) INSTANT(PageChangeEffect.INSTANT)
} }
internal fun GUI<*>.changePage( internal fun GUIInstance<*>.changePage(
effect: PageChangeEffect, effect: PageChangeEffect,
fromPage: GUIPage<*>, fromPage: GUIPage<*>,
toPage: GUIPage<*> toPage: GUIPage<*>
@@ -51,7 +51,7 @@ internal fun GUI<*>.changePage(
PageChangeEffect.SLIDE_HORIZONTALLY -> { PageChangeEffect.SLIDE_HORIZONTALLY -> {
val width = data.guiType.dimensions.width val width = gui.data.guiType.dimensions.width
changePageEffect(fromPageInt, toPageInt, width) { currentOffset, ifInverted -> changePageEffect(fromPageInt, toPageInt, width) { currentOffset, ifInverted ->
if (ifInverted) { if (ifInverted) {
@@ -67,7 +67,7 @@ internal fun GUI<*>.changePage(
PageChangeEffect.SLIDE_VERTICALLY -> { PageChangeEffect.SLIDE_VERTICALLY -> {
val height = data.guiType.dimensions.height val height = gui.data.guiType.dimensions.height
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted -> changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
if (ifInverted) { if (ifInverted) {
@@ -83,7 +83,7 @@ internal fun GUI<*>.changePage(
PageChangeEffect.SWIPE_HORIZONTALLY -> { PageChangeEffect.SWIPE_HORIZONTALLY -> {
val width = data.guiType.dimensions.width val width = gui.data.guiType.dimensions.width
changePageEffect(fromPageInt, toPageInt, width) { currentOffset, ifInverted -> changePageEffect(fromPageInt, toPageInt, width) { currentOffset, ifInverted ->
if (ifInverted) { if (ifInverted) {
@@ -97,7 +97,7 @@ internal fun GUI<*>.changePage(
PageChangeEffect.SWIPE_VERTICALLY -> { PageChangeEffect.SWIPE_VERTICALLY -> {
val height = data.guiType.dimensions.height val height = gui.data.guiType.dimensions.height
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted -> changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
if (ifInverted) { if (ifInverted) {
@@ -112,7 +112,7 @@ internal fun GUI<*>.changePage(
} }
} }
internal fun GUI<*>.changeGUI( internal fun GUIInstance<*>.changeGUI(
effect: InventoryChangeEffect, effect: InventoryChangeEffect,
fromPage: GUIPage<*>, fromPage: GUIPage<*>,
toPage: GUIPage<*> toPage: GUIPage<*>

View File

@@ -1,6 +1,8 @@
package net.axay.kspigot.gui.elements package net.axay.kspigot.gui.elements
import net.axay.kspigot.gui.* import net.axay.kspigot.gui.ForInventory
import net.axay.kspigot.gui.GUIClickEvent
import net.axay.kspigot.gui.GUIElement
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
open class GUIButton<T : ForInventory>( open class GUIButton<T : ForInventory>(

View File

@@ -10,16 +10,16 @@ class GUIButtonInventoryChange<T : ForInventory>(
onChange: ((GUIClickEvent<T>) -> Unit)? onChange: ((GUIClickEvent<T>) -> Unit)?
) : GUIButton<T>(icon, { ) : GUIButton<T>(icon, {
val changeToGUI = changeToGUICallback.invoke() val changeToGUI = changeToGUICallback.invoke().getInstance(it.player)
val effect = (changeToGUI.data.transitionTo ?: it.gui.data.transitionFrom) val effect = (changeToGUI.gui.data.transitionTo ?: it.guiInstance.gui.data.transitionFrom)
?: InventoryChangeEffect.INSTANT ?: InventoryChangeEffect.INSTANT
val changeToPage = changeToGUI.getPage(changeToPageInt) ?: changeToGUI.currentPage val changeToPage = changeToGUI.getPage(changeToPageInt) ?: changeToGUI.currentPage
changeToGUI.changeGUI(effect, it.gui.currentPage, changeToPage) changeToGUI.changeGUI(effect, it.guiInstance.currentPage, changeToPage)
it.bukkitEvent.whoClicked.openGUI(changeToGUI) it.player.openGUIInstance(changeToGUI)
onChange?.invoke(it) onChange?.invoke(it)

View File

@@ -9,14 +9,19 @@ class GUIButtonPageChange<T : ForInventory>(
onChange: ((GUIClickEvent<T>) -> Unit)? onChange: ((GUIClickEvent<T>) -> Unit)?
) : GUIButton<T>(icon, { ) : GUIButton<T>(icon, {
val currentPage = it.gui.currentPage val currentPage = it.guiInstance.currentPage
val newPage = it.gui.getPage(calculator.calculateNewPage(it.gui.currentPageInt, it.gui.data.pages.keys)) val newPage = it.guiInstance.getPage(
calculator.calculateNewPage(
it.guiInstance.currentPageInt,
it.guiInstance.gui.data.pages.keys
)
)
if (newPage != null) { if (newPage != null) {
val effect = (newPage.transitionTo ?: currentPage.transitionFrom) val effect = (newPage.transitionTo ?: currentPage.transitionFrom)
?: PageChangeEffect.INSTANT ?: PageChangeEffect.INSTANT
it.gui.changePage(effect, currentPage, newPage) it.guiInstance.changePage(effect, currentPage, newPage)
onChange?.invoke(it) onChange?.invoke(it)

View File

@@ -1,6 +1,8 @@
package net.axay.kspigot.gui.elements package net.axay.kspigot.gui.elements
import net.axay.kspigot.gui.* import net.axay.kspigot.gui.ForInventory
import net.axay.kspigot.gui.GUIClickEvent
import net.axay.kspigot.gui.GUIElement
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
class GUIPlaceholder<T : ForInventory>( class GUIPlaceholder<T : ForInventory>(

View File

@@ -16,17 +16,17 @@ class GUISpaceCompoundElement<T : ForInventory, E> internal constructor(
compound.onClickElement(clickEvent) compound.onClickElement(clickEvent)
} }
override fun startUsing(gui: GUI<*>) = compound.registerGUI(gui) override fun startUsing(gui: GUIInstance<*>) = compound.registerGUI(gui)
override fun stopUsing(gui: GUI<*>) = compound.unregisterGUI(gui) override fun stopUsing(gui: GUIInstance<*>) = compound.unregisterGUI(gui)
} }
class GUIRectSpaceCompound<T : ForInventory, E>( class GUIRectSpaceCompound<T : ForInventory, E>(
invType: GUIType<T>, invType: GUIType<T>,
iconGenerator: (E) -> ItemStack, iconGenerator: (E) -> ItemStack,
onClick: (GUIClickEvent<T>, E) -> Unit, onClick: (GUIClickEvent<T>, E) -> Unit,
internal val compoundWidth: Int internal val compoundWidth: Int
) : AbstractGUISpaceCompound<T, E>(invType, iconGenerator, onClick) { ) : AbstractGUISpaceCompound<T, E>(invType, iconGenerator, onClick) {
override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) =
@@ -35,9 +35,9 @@ class GUIRectSpaceCompound<T : ForInventory, E>(
} }
class GUISpaceCompound<T : ForInventory, E>( class GUISpaceCompound<T : ForInventory, E>(
invType: GUIType<T>, invType: GUIType<T>,
iconGenerator: (E) -> ItemStack, iconGenerator: (E) -> ItemStack,
onClick: (GUIClickEvent<T>, E) -> Unit onClick: (GUIClickEvent<T>, E) -> Unit
) : AbstractGUISpaceCompound<T, E>(invType, iconGenerator, onClick) { ) : AbstractGUISpaceCompound<T, E>(invType, iconGenerator, onClick) {
override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = false override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = false
@@ -45,9 +45,9 @@ class GUISpaceCompound<T : ForInventory, E>(
} }
abstract class AbstractGUISpaceCompound<T : ForInventory, E> internal constructor( abstract class AbstractGUISpaceCompound<T : ForInventory, E> internal constructor(
val guiType: GUIType<T>, val guiType: GUIType<T>,
private val iconGenerator: (E) -> ItemStack, private val iconGenerator: (E) -> ItemStack,
private val onClick: (GUIClickEvent<T>, E) -> Unit private val onClick: (GUIClickEvent<T>, E) -> Unit
) { ) {
private val content = ArrayList<E>() private val content = ArrayList<E>()
@@ -59,7 +59,7 @@ abstract class AbstractGUISpaceCompound<T : ForInventory, E> internal constructo
private var contentSort: () -> Unit = { } private var contentSort: () -> Unit = { }
private val registeredGUIs = HashSet<GUI<*>>() private val registeredGUIs = HashSet<GUIInstance<*>>()
private fun contentAtSlot(slot: Int) = currentContent.getOrNull(internalSlots.indexOf(slot)) private fun contentAtSlot(slot: Int) = currentContent.getOrNull(internalSlots.indexOf(slot))
@@ -123,11 +123,11 @@ abstract class AbstractGUISpaceCompound<T : ForInventory, E> internal constructo
internalSlots.sort() internalSlots.sort()
} }
internal fun registerGUI(gui: GUI<*>) { internal fun registerGUI(gui: GUIInstance<*>) {
registeredGUIs += gui registeredGUIs += gui
} }
internal fun unregisterGUI(gui: GUI<*>) { internal fun unregisterGUI(gui: GUIInstance<*>) {
registeredGUIs -= gui registeredGUIs -= gui
} }