From 201042fd2b7a83224d6dc46473ffb6bcb4f9b2e2 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 00:30:03 +0100 Subject: [PATCH 01/15] Add First Minestiom Prototype --- gradle/libs.versions.toml | 8 +- .../component/DefaultComponentBuilder.java | 2 +- .../build.gradle | 36 +++ .../inventoryframework/IFInventoryListener.kt | 93 ++++++ .../MinestomViewContainer.kt | 158 ++++++++++ .../inventoryframework/MinestomViewer.kt | 104 +++++++ .../me/devnatan/inventoryframework/View.kt | 28 ++ .../devnatan/inventoryframework/ViewFrame.kt | 292 ++++++++++++++++++ .../component/MinestomIemComponentBuilder.kt | 224 ++++++++++++++ .../context/CloseContext.kt | 111 +++++++ .../inventoryframework/context/Context.kt | 56 ++++ .../inventoryframework/context/OpenContext.kt | 141 +++++++++ .../context/RenderContext.kt | 149 +++++++++ .../context/SlotClickContext.kt | 121 ++++++++ .../inventoryframework/context/SlotContext.kt | 172 +++++++++++ .../context/SlotRenderContext.kt | 78 +++++ .../internal/MinestomElementFactory.kt | 155 ++++++++++ .../internal/MinestomTaskJobImpl.kt | 31 ++ .../logging/BukkitLogger.kt | 29 ++ .../pipeline/CancelledCloseInterceptor.kt | 15 + .../pipeline/GlobalClickInterceptor.kt | 24 ++ .../pipeline/ItemClickInterceptor.kt | 28 ++ .../pipeline/ItemCloseOnClickInterceptor.kt | 28 ++ .../inventoryframework/util/IsTypeOf.kt | 7 + .../inventoryframework/IFViewFrame.java | 2 +- .../inventoryframework/PlatformView.java | 4 + .../context/PlatformConfinedContext.java | 4 +- settings.gradle | 2 + 28 files changed, 2098 insertions(+), 4 deletions(-) create mode 100644 inventory-framework-platform-minestom/build.gradle create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewContainer.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/Context.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/RenderContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomTaskJobImpl.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemCloseOnClickInterceptor.kt create mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7f08771d..c5f2366f8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,10 +5,12 @@ paperSpigot = "1.20.1-R0.1-SNAPSHOT" junit = "5.10.1" mockito = "4.11.0" adventure-api = "4.14.0" -kotlin = "2.0.21" +kotlin = "2.1.0" plugin-shadowjar = "8.1.1" plugin-spotless = "6.25.0" plugin-bukkit = "0.6.0" +minestom = "87f6524aeb" + [libraries.spigot] module = "org.spigotmc:spigot-api" @@ -42,6 +44,10 @@ version.ref = "mockito" module = "net.kyori:adventure-api" version.ref = "adventure-api" +[libraries.minestom] +module = "net.minestom:minestom-snapshots" +version.ref = "minestom" + [plugins] shadowjar = { id = "com.github.johnrengelman.shadow", version.ref = "plugin-shadowjar" } spotless = { id = "com.diffplug.spotless", version.ref = "plugin-spotless" } diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index e25ddb843..818ba2c84 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -13,7 +13,7 @@ import org.jetbrains.annotations.NotNull; @SuppressWarnings("unchecked") -abstract class DefaultComponentBuilder, C extends IFContext> +public abstract class DefaultComponentBuilder, C extends IFContext> implements ComponentBuilder { protected Ref reference; diff --git a/inventory-framework-platform-minestom/build.gradle b/inventory-framework-platform-minestom/build.gradle new file mode 100644 index 000000000..cc7e7bcf6 --- /dev/null +++ b/inventory-framework-platform-minestom/build.gradle @@ -0,0 +1,36 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + alias(libs.plugins.shadowjar) + alias(libs.plugins.kotlin) +} + +apply from: '../library.gradle' +apply from: '../publish.gradle' + +dependencies { + api projects.inventoryFrameworkPlatform + runtimeOnly projects.inventoryFrameworkAnvilInput + compileOnly libs.minestom + testCompileOnly libs.minestom + testImplementation projects.inventoryFrameworkApi + testImplementation projects.inventoryFrameworkTest +} + +shadowJar { + archiveBaseName.set('inventory-framework') + archiveAppendix.set('bukkit') + + dependencies { + include(project(":inventory-framework-platform")) + include(project(":inventory-framework-anvil-input")) + } +} + + +java { + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} \ No newline at end of file diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt new file mode 100644 index 000000000..53f35a649 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt @@ -0,0 +1,93 @@ +package me.devnatan.inventoryframework + +import me.devnatan.inventoryframework.context.IFCloseContext +import me.devnatan.inventoryframework.context.IFContext +import me.devnatan.inventoryframework.context.IFRenderContext +import me.devnatan.inventoryframework.context.IFSlotClickContext +import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases +import net.minestom.server.MinecraftServer +import net.minestom.server.entity.Player +import net.minestom.server.event.EventFilter +import net.minestom.server.event.EventNode +import net.minestom.server.event.inventory.InventoryCloseEvent +import net.minestom.server.event.inventory.InventoryPreClickEvent +import net.minestom.server.event.item.ItemDropEvent +import net.minestom.server.event.item.PickupItemEvent +import net.minestom.server.inventory.PlayerInventory + +internal class IFInventoryListener( + private val viewFrame: ViewFrame +) { + + init { + val handler = MinecraftServer.getGlobalEventHandler() + val inventoryNode = EventNode.type("IF-inventory", EventFilter.INVENTORY) + .setPriority(10) + .addListener(InventoryPreClickEvent::class.java, this::onInventoryClick) + .addListener(InventoryCloseEvent::class.java, this::onInventoryClose) + val playerNode = EventNode.type("IF-player", EventFilter.PLAYER) + { _, p -> viewFrame.getViewer(p) != null } + .setPriority(10) + .addListener(ItemDropEvent::class.java, this::onItemDrop) + val entityEvent = EventNode.type("IF-entity", EventFilter.ENTITY) + { _, e -> e is Player && viewFrame.getViewer(e) != null } + .setPriority(10) + .addListener(PickupItemEvent::class.java, this::onItemPickup) + handler.addChild(inventoryNode) + handler.addChild(playerNode) + handler.addChild(entityEvent) + } + + fun onInventoryClick(event: InventoryPreClickEvent) { + if (event.isCancelled) return + + val player = event.player + val viewer = viewFrame.getViewer(player) ?: return + + val context: IFRenderContext = viewer.activeContext + val clickedComponent: me.devnatan.inventoryframework.component.Component = + context.getComponentsAt(event.slot).stream() + .filter { obj: me.devnatan.inventoryframework.component.Component -> obj.isVisible } + .findFirst() + .orElse(null) + val clickedContainer = if (event.inventory is PlayerInventory) + viewer.selfContainer + else + context.getContainer() + + val root: RootView = context.getRoot() + val clickContext: IFSlotClickContext = root.elementFactory + .createSlotClickContext(event.slot, viewer, clickedContainer, clickedComponent, event, false) + + root.pipeline.execute(StandardPipelinePhases.CLICK, clickContext) + } + + fun onInventoryClose(event: InventoryCloseEvent) { + val player: Player = event.player + val viewer = viewFrame.getViewer(player) ?: return + + val context: IFRenderContext = viewer.activeContext + val root: RootView = context.getRoot() + val closeContext: IFCloseContext = root.elementFactory.createCloseContext(viewer, context) + + root.pipeline.execute(StandardPipelinePhases.CLOSE, closeContext) + } + + fun onItemPickup(event: PickupItemEvent) { + val viewer = viewFrame.getViewer(event.entity as Player) ?: return + + val context: IFContext = viewer.activeContext + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_PICKUP)) return + + event.isCancelled = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP) + } + + fun onItemDrop(event: ItemDropEvent) { + val viewer = viewFrame.getViewer(event.getPlayer()) ?: return + + val context: IFContext = viewer.activeContext + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) return + + event.isCancelled = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewContainer.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewContainer.kt new file mode 100644 index 000000000..d6612b9dd --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewContainer.kt @@ -0,0 +1,158 @@ +package me.devnatan.inventoryframework + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer +import net.minestom.server.entity.Player +import net.minestom.server.inventory.Inventory +import net.minestom.server.inventory.InventoryType +import net.minestom.server.inventory.PlayerInventory +import net.minestom.server.item.ItemStack +import java.util.* + +class MinestomViewContainer( + private val inventory: Inventory, shared: Boolean, private val type: ViewType, + private val proxied: Boolean +) : + ViewContainer { + val isShared: Boolean = shared + + fun getInventory(): Inventory { + return inventory + } + + override fun isProxied(): Boolean { + return proxied + } + + override fun getTitle(): String { + val diffTitle: Boolean = inventory.viewers.stream() + .map { player -> + (player.openInventory as? Inventory)?.title + ?.let { PlainTextComponentSerializer.plainText().serialize(it) } ?: "" + } + .distinct() + .findAny() + .isPresent + + check(!(diffTitle && isShared)) { "Cannot get unique title of shared inventory" } + val openInventory = inventory.viewers.first().openInventory + return (openInventory as? Inventory)?.title + ?.let { PlainTextComponentSerializer.plainText().serialize(it) } ?: "" + } + + override fun getTitle(viewer: Viewer): String { + return ((viewer as MinestomViewer).player.openInventory as? Inventory)?.title + ?.let { PlainTextComponentSerializer.plainText().serialize(it) } ?: "" + } + + override fun getType(): ViewType { + return type + } + + override fun getRowsCount(): Int { + return size / columnsCount + } + + override fun getColumnsCount(): Int { + return type.columns + } + + override fun renderItem(slot: Int, item: Any) { + requireSupportedItem(item) + inventory.setItemStack(slot, item as ItemStack) + } + + override fun removeItem(slot: Int) { + inventory.setItemStack(slot, ItemStack.AIR) + } + + override fun matchesItem(slot: Int, item: Any?, exactly: Boolean): Boolean { + requireSupportedItem(item) + val target: ItemStack = inventory.getItemStack(slot) ?: return item == null + if (item is ItemStack) return if (exactly) target == item else target.isSimilar(item as ItemStack) + + return false + } + + override fun isSupportedItem(item: Any?): Boolean { + return item == null || item is ItemStack + } + + private fun requireSupportedItem(item: Any?) { + if (isSupportedItem(item)) return + + throw IllegalStateException( + "Unsupported item type: " + item!!.javaClass.name + ) + } + + override fun hasItem(slot: Int): Boolean { + return !inventory.getItemStack(slot).isAir + } + + override fun getSize(): Int { + return inventory.size + } + + override fun getSlotsCount(): Int { + return size - 1 + } + + override fun getFirstSlot(): Int { + return 0 + } + + override fun getLastSlot(): Int { + val resultSlots = getType().resultSlots + var lastSlot = slotsCount + if (resultSlots != null) { + for (resultSlot in resultSlots) { + if (resultSlot == lastSlot) lastSlot-- + } + } + + return lastSlot + } + + override fun changeTitle(title: String?, target: Viewer) { + changeTitle(title?.let { Component.text(it) } ?: Component.empty(), (target as MinestomViewer).player) + } + + fun changeTitle(title: Component, target: Player) { + val open: Inventory = target.openInventory as? Inventory ?: return + if (inventory.inventoryType == InventoryType.CRAFTING || inventory.inventoryType == InventoryType.CRAFTER_3X3) return + open.setTitle(title) + } + + override fun isEntityContainer(): Boolean { + return inventory is PlayerInventory + } + + override fun open(viewer: Viewer) { + viewer.open(this) + } + + override fun close() { + inventory.viewers.forEach(Player::closeInventory) + } + + override fun close(viewer: Viewer) { + viewer.close() + } + + override fun equals(o: Any?): Boolean { + if (this === o) return true + if (o == null || javaClass != o.javaClass) return false + val that = o as MinestomViewContainer + return isShared == that.isShared && inventory == that.inventory + && getType() == that.getType() + } + + override fun hashCode(): Int { + return Objects.hash(inventory, isShared, getType()) + } + + override fun toString(): String { + return "BukkitViewContainer{" + "inventory=" + inventory + ", shared=" + isShared + ", type=" + type + '}' + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt new file mode 100644 index 000000000..666019c32 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt @@ -0,0 +1,104 @@ +package me.devnatan.inventoryframework + +import me.devnatan.inventoryframework.context.IFRenderContext +import net.minestom.server.entity.Player +import net.minestom.server.inventory.Inventory +import java.util.* + +class MinestomViewer(val player: Player, activeContext: IFRenderContext) : Viewer { + private var selfContainer: ViewContainer? = null + private var activeContext: IFRenderContext + private val previousContexts: Deque = LinkedList() + private var lastInteractionInMillis: Long = 0 + private var transitioning = false + + init { + this.activeContext = activeContext + } + + override fun getActiveContext(): IFRenderContext { + return activeContext + } + + override fun setActiveContext(context: IFRenderContext) { + this.activeContext = context + } + + override fun getId(): String { + return player.uuid.toString() + } + + override fun open(container: ViewContainer) { + player.openInventory((container as MinestomViewContainer).getInventory()) + } + + override fun close() { + player.closeInventory() + } + + override fun getSelfContainer(): ViewContainer { + if (selfContainer == null) selfContainer = MinestomViewContainer( + player.openInventory as Inventory, getActiveContext().isShared(), ViewType.PLAYER, false + ) + return selfContainer!! + } + + override fun getLastInteractionInMillis(): Long { + return lastInteractionInMillis + } + + override fun setLastInteractionInMillis(lastInteractionInMillis: Long) { + this.lastInteractionInMillis = lastInteractionInMillis + } + + override fun isBlockedByInteractionDelay(): Boolean { + val configuredDelay: Long = activeContext.getConfig().getInteractionDelayInMillis() + if (configuredDelay <= 0 || getLastInteractionInMillis() <= 0) return false + + return getLastInteractionInMillis() + configuredDelay >= System.currentTimeMillis() + } + + override fun isTransitioning(): Boolean { + return transitioning + } + + override fun setTransitioning(transitioning: Boolean) { + this.transitioning = transitioning + } + + override fun getPreviousContext(): IFRenderContext? { + return previousContexts.peekLast() + } + + override fun setPreviousContext(previousContext: IFRenderContext) { + previousContexts.add(previousContext) + } + + override fun unsetPreviousContext() { + previousContexts.pollLast() + } + + override fun getPlatformInstance(): Any { + return player + } + + override fun equals(o: Any?): Boolean { + if (this === o) return true + if (o == null || javaClass != o.javaClass) return false + val that = o as MinestomViewer + return player == that.player + } + + override fun hashCode(): Int { + return player.hashCode() + } + + override fun toString(): String { + return ("BukkitViewer{" + + "player=" + player + + ", selfContainer=" + selfContainer + + ", lastInteractionInMillis=" + lastInteractionInMillis + + ", isTransitioning=" + transitioning + + "}") + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt new file mode 100644 index 000000000..fe60f66b9 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt @@ -0,0 +1,28 @@ +package me.devnatan.inventoryframework + +import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder +import me.devnatan.inventoryframework.context.* +import me.devnatan.inventoryframework.pipeline.* +import net.minestom.server.MinecraftServer +import net.minestom.server.entity.Player +import org.jetbrains.annotations.ApiStatus.OverrideOnly + +/** + * Bukkit platform [PlatformView] implementation. + */ +@OverrideOnly +class View : + PlatformView() { + + public override fun registerPlatformInterceptors() { + val pipeline: Pipeline = pipeline + pipeline.intercept(StandardPipelinePhases.CLICK, ItemClickInterceptor()) + pipeline.intercept(StandardPipelinePhases.CLICK, GlobalClickInterceptor()) + pipeline.intercept(StandardPipelinePhases.CLICK, ItemCloseOnClickInterceptor()) + pipeline.intercept(StandardPipelinePhases.CLOSE, CancelledCloseInterceptor()) + } + + override fun nextTick(task: Runnable) { + MinecraftServer.getSchedulerManager().scheduleNextTick(task) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt new file mode 100644 index 000000000..6482bce80 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt @@ -0,0 +1,292 @@ +package me.devnatan.inventoryframework + +import me.devnatan.inventoryframework.context.EndlessContextInfo +import me.devnatan.inventoryframework.feature.DefaultFeatureInstaller +import me.devnatan.inventoryframework.feature.Feature +import me.devnatan.inventoryframework.feature.FeatureInstaller +import me.devnatan.inventoryframework.internal.MinestomElementFactory +import me.devnatan.inventoryframework.internal.PlatformUtils +import net.minestom.server.entity.Player +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.ApiStatus.Experimental +import java.util.function.UnaryOperator + +class ViewFrame private constructor() : IFViewFrame() { + + private val featureInstaller: FeatureInstaller = DefaultFeatureInstaller( + this + ) + + + // region Opening + /** + * Opens a view to a player. + * + * @param viewClass The target view to be opened. + * @param player The player that the view will be open to. + * @return The id of the newly created [IFContext]. + */ + fun open(viewClass: Class, player: Player): String { + return open(viewClass, player, null) + } + + /** + * Opens a view to a player with initial data. + * + * @param viewClass The target view to be opened. + * @param player The player that the view will be open to. + * @param initialData The initial data. + * @return The id of the newly created [IFContext]. + */ + fun open(viewClass: Class, player: Player, initialData: Any?): String { + return open(viewClass, listOf(player), initialData) + } + + /** + * Opens a view to more than one player. + * + * + * These players will see the same inventory and share the same context. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param viewClass The target view to be opened. + * @param players The players that the view will be open to. + * @return The id of the newly created [IFContext]. + */ + @Experimental + fun open(viewClass: Class, players: Collection): String { + return open(viewClass, players, null) + } + + /** + * Opens a view to more than one player with initial data. + * + * + * These players will see the same inventory and share the same context. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param viewClass The target view to be opened. + * @param players The players that the view will be open to. + * @param initialData The initial data. + * @return The id of the newly created [IFContext]. + */ + @Experimental + fun open( + viewClass: Class, + players: Collection, + initialData: Any? + ): String { + return internalOpen(viewClass, players, initialData) + } + + /** + * Opens an already active context to a player. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param contextId The id of the context. + * @param player Who the context will be open to. + */ + @Experimental + fun openActive( + viewClass: Class, contextId: String, player: Player + ) { + openActive(viewClass, contextId, player, null) + } + + /** + * Opens an already active context to a player. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param contextId The id of the context. + * @param player Who the context will be open to. + * @param initialData Initial data to pass to [PlatformView.onViewerAdded]. + */ + @Experimental + fun openActive( + viewClass: Class, + contextId: String, + player: Player, + initialData: Any? + ) { + internalOpenActiveContext(viewClass, contextId, player, initialData) + } + + /** + * Opens an already active context to a player. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param endlessContextInfo The id of the context. + * @param player Who the context will be open to. + */ + @Experimental + fun openEndless(endlessContextInfo: EndlessContextInfo, player: Player) { + openEndless(endlessContextInfo, player, null) + } + + /** + * Opens an already active context to a player. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param endlessContextInfo The id of the context. + * @param player Who the context will be open to. + * @param initialData Initial data to pass to [PlatformView.onViewerAdded]. + */ + @Experimental + fun openEndless( + endlessContextInfo: EndlessContextInfo, player: Player, initialData: Any? + ) { + openActive( + endlessContextInfo.getView().javaClass as Class, + endlessContextInfo.getContextId(), + player, + initialData + ) + } + + // endregion + override fun register(): ViewFrame { + check(!isRegistered) { "This view frame is already registered" } + + isRegistered = true + pipeline.execute(FRAME_REGISTERED, this) + initializeViews() + IFInventoryListener(this) + return this + } + + override fun unregister() { + if (!isRegistered) return + + // Locks new operations while unregistering + isRegistered = false + + val iterator: MutableIterator = registeredViews.values.iterator() + while (iterator.hasNext()) { + val view = iterator.next() + try { + view.closeForEveryone() + } catch (ignored: RuntimeException) { + } + iterator.remove() + } + pipeline.execute(FRAME_UNREGISTERED, this) + } + + private fun initializeViews() { + for ((_, view) in getRegisteredViews()) { + try { + view.internalInitialization(this) + view.isInitialized = true + } catch (exception: RuntimeException) { + view.isInitialized = false + LOGGER.severe( + String.format( + "An error occurred while enabling view %s: %s", + view.javaClass.name, exception + ) + ) + exception.printStackTrace() + } + } + } + + // endregion + /** + * *** This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. *** + */ + @ApiStatus.Internal + fun getViewer(player: Player): Viewer? { + return viewerById[player.uuid.toString()] + } + + /** + * Installs a feature. + * + * @param feature The feature to be installed. + * @param configure The feature configuration. + * @param The feature configuration type. + * @param The feature value instance type. + * @return An instance of the installed feature. + */ + fun install( + feature: Feature, configure: UnaryOperator + ): ViewFrame { + featureInstaller.install(feature, configure) + IFDebug.debug("Feature %s installed", feature.name()) + return this + } + + /** + * Installs a feature with no specific configuration. + * + * @param feature The feature to be installed. + * @return This view frame. + */ + fun install(feature: Feature<*, *, ViewFrame?>): ViewFrame { + install(feature, UnaryOperator.identity()) + return this + } + + /** + * Disables bStats metrics tracking. + * + * + * InventoryFramework use bStats metrics to obtain some information from servers that use it as + * a library, such as: number of players, version, software, etc. + * + * + * **No sensitive information is tracked.** + * + * @return This view frame. + */ + fun disableMetrics(): ViewFrame { + System.setProperty(BSTATS_SYSTEM_PROP, java.lang.Boolean.FALSE.toString()) + return this + } + + companion object { + private const val BSTATS_SYSTEM_PROP = "inventory-framework.enable-bstats" + private const val BSTATS_PROJECT_ID = 15518 + private const val PLUGIN_FQN = "me.devnatan.inventoryframework.runtime.InventoryFramework" + + private const val RELOCATION_MESSAGE = + ("Inventory Framework is running as a shaded non-relocated library. It's extremely recommended that " + + "you relocate the library package. Learn more about on docs: " + + "https://github.com/DevNatan/inventory-framework/wiki/Installation#preventing-library-conflicts") + + init { + PlatformUtils.setFactory(MinestomElementFactory()) + } + + /** + * Creates a new ViewFrame. + * + * @param owner The plugin that owns this view frame. + * @return A new ViewFrame instance. + */ + fun create(): ViewFrame { + return ViewFrame() + } + + private val LOGGER = java.util.logging.Logger.getLogger("IF") + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt new file mode 100644 index 000000000..b91eab3de --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt @@ -0,0 +1,224 @@ +package me.devnatan.inventoryframework.component + +import me.devnatan.inventoryframework.Ref +import me.devnatan.inventoryframework.ViewContainer +import me.devnatan.inventoryframework.VirtualView +import me.devnatan.inventoryframework.context.Context +import me.devnatan.inventoryframework.context.IFRenderContext +import me.devnatan.inventoryframework.context.IFSlotClickContext +import me.devnatan.inventoryframework.context.IFSlotContext +import me.devnatan.inventoryframework.context.IFSlotRenderContext +import me.devnatan.inventoryframework.context.SlotClickContext +import me.devnatan.inventoryframework.context.SlotContext +import me.devnatan.inventoryframework.context.SlotRenderContext +import me.devnatan.inventoryframework.state.State +import me.devnatan.inventoryframework.utils.SlotConverter +import net.minestom.server.item.ItemStack +import java.util.function.Consumer +import java.util.function.Predicate +import java.util.function.Supplier + +class MinestomIemComponentBuilder private constructor( + root: VirtualView, + slot: Int, + item: ItemStack?, + renderHandler: Consumer?, + clickHandler: Consumer?, + updateHandler: Consumer?, + reference: Ref?, + data: Map, + cancelOnClick: Boolean, + closeOnClick: Boolean, + updateOnClick: Boolean, + watchingStates: Set>, + isManagedExternally: Boolean, + displayCondition: Predicate? +) : DefaultComponentBuilder( + reference, + data, + cancelOnClick, + closeOnClick, + updateOnClick, + watchingStates, + isManagedExternally, + displayCondition + ), ItemComponentBuilder, + ComponentFactory { + private val root: VirtualView = root + private var slot: Int + private var item: ItemStack? + private var renderHandler: Consumer? + private var clickHandler: Consumer? + private var updateHandler: Consumer? + + constructor(root: VirtualView) : this( + root, + -1, + null, + null, + null, + null, + null, + HashMap(), + false, + false, + false, + LinkedHashSet>(), + false, + null + ) + + init { + this.slot = slot + this.item = item + this.renderHandler = renderHandler + this.clickHandler = clickHandler + this.updateHandler = updateHandler + } + + override fun toString(): String { + return ("BukkitItemComponentBuilder{" + + "slot=" + slot + + ", item=" + item + + ", renderHandler=" + renderHandler + + ", clickHandler=" + clickHandler + + ", updateHandler=" + updateHandler + + "} " + super.toString()) + } + + override fun isContainedWithin(position: Int): Boolean { + return position == slot + } + + /** + * {@inheritDoc} + */ + override fun withSlot(slot: Int): MinestomIemComponentBuilder { + this.slot = slot + return this + } + + override fun withSlot(row: Int, column: Int): MinestomIemComponentBuilder { + val container: ViewContainer = (root as IFRenderContext).getContainer() + return withSlot(SlotConverter.convertSlot(row, column, container.getRowsCount(), container.getColumnsCount())) + } + + /** + * Defines the item that will be used as fallback for rendering in the slot where this item is + * positioned. The fallback item is always static. + * + * @param item The new fallback item stack. + * @return This item builder. + */ + fun withItem(item: ItemStack?): MinestomIemComponentBuilder { + this.item = item + return this + } + + /** + * Called when the item is rendered. + * + * + * This handler is called every time the item or the view that owns it is updated. + * + * @param renderHandler The render handler. + * @return This item builder. + */ + fun onRender(renderHandler: Consumer?): MinestomIemComponentBuilder { + this.renderHandler = renderHandler as? Consumer + return this + } + + /** + * Dynamic rendering of a specific item. + * + * + * This handler is called every time the item or the view that owns it is updated. + * + * @param renderFactory The render handler. + * @return This item builder. + */ + fun renderWith(renderFactory: Supplier): MinestomIemComponentBuilder { + return onRender { render: SlotRenderContext -> + render.item = renderFactory.get() + } + } + + /** + * Called when a player clicks on the item. + * + * + * This handler works on any container that the actor has access to and only works if the + * interaction has not been cancelled. + * + * @param clickHandler The click handler. + * @return This item builder. + */ + fun onClick(clickHandler: Consumer?): MinestomIemComponentBuilder { + this.clickHandler = clickHandler as? Consumer + return this + } + + /** + * Called when a player clicks on the item. + * + * + * This handler works on any container that the actor has access to and only works if the + * interaction has not been cancelled. + * + * @param clickHandler The click handler. + * @return This item builder. + */ + fun onClick(clickHandler: Runnable?): MinestomIemComponentBuilder { + return onClick(if (clickHandler == null) null else Consumer { `$`: SlotClickContext? -> clickHandler.run() }) + } + + /** + * Called when the item is updated. + * + * @param updateHandler The update handler. + * @return This item builder. + */ + fun onUpdate(updateHandler: Consumer?): MinestomIemComponentBuilder { + this.updateHandler = updateHandler as? Consumer + return this + } + + override fun create(): Component { + return ItemComponent( + root, + slot, + item, + cancelOnClick, + closeOnClick, + displayCondition, + renderHandler, + updateHandler, + clickHandler, + watchingStates, + isManagedExternally, + updateOnClick, + false, + reference + ) + } + + override fun copy(): MinestomIemComponentBuilder { + return MinestomIemComponentBuilder( + root, + slot, + item, + renderHandler, + clickHandler, + updateHandler, + reference, + data, + cancelOnClick, + closeOnClick, + updateOnClick, + watchingStates, + isManagedExternally, + displayCondition + ) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt new file mode 100644 index 000000000..02c77b61d --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt @@ -0,0 +1,111 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.MinestomViewer +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfig +import me.devnatan.inventoryframework.ViewContainer +import me.devnatan.inventoryframework.Viewer +import me.devnatan.inventoryframework.state.State +import me.devnatan.inventoryframework.state.StateValue +import me.devnatan.inventoryframework.state.StateWatcher +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player +import org.jetbrains.annotations.UnmodifiableView +import java.util.* + +class CloseContext(subject: Viewer, private val parent: IFRenderContext) : + PlatformConfinedContext(), + IFCloseContext, Context { + private val subject: Viewer = subject + override val player: Player = (subject as MinestomViewer).player + + private var cancelled = false + + override val allPlayers: List + get() = getParent().allPlayers + + override fun updateTitleForPlayer(title: Component, player: Player) { + getParent().updateTitleForPlayer(title, player) + } + + override fun resetTitleForPlayer(player: Player) { + getParent().resetTitleForPlayer(player) + } + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + } + + override fun getViewer(): Viewer { + return subject + } + + override fun getParent(): RenderContext { + return parent as RenderContext + } + + override fun getId(): UUID { + return getParent().id + } + + override fun getConfig(): ViewConfig { + return getParent().config + } + + override fun getContainer(): ViewContainer { + return getParent().container + } + + override fun getRoot(): View { + return getParent().getRoot() + } + + override fun getInitialData(): Any { + return getParent().initialData + } + + override fun setInitialData(initialData: Any) { + getParent().initialData = initialData + } + + override fun getStateValues(): @UnmodifiableView MutableMap? { + return getParent().stateValues + } + + override fun initializeState(id: Long, value: StateValue) { + getParent().initializeState(id, value) + } + + override fun watchState(id: Long, listener: StateWatcher) { + getParent().watchState(id, listener) + } + + override fun getRawStateValue(state: State<*>?): Any { + return getParent().getRawStateValue(state) + } + + override fun getInternalStateValue(state: State<*>): StateValue { + return getParent().getInternalStateValue(state) + } + + override fun getUninitializedStateValue(stateId: Long): StateValue { + return getParent().getUninitializedStateValue(stateId) + } + + override fun updateState(id: Long, value: Any) { + getParent().updateState(id, value) + } + + override fun toString(): String { + return ("CloseContext{" + "subject=" + + subject + ", player=" + + player + ", parent=" + + parent + ", cancelled=" + + cancelled + "} " + + super.toString()) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/Context.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/Context.kt new file mode 100644 index 000000000..4f7f6802b --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/Context.kt @@ -0,0 +1,56 @@ +package me.devnatan.inventoryframework.context + +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player +import org.jetbrains.annotations.ApiStatus.Experimental + +interface Context : IFConfinedContext { + /** + * The player for the current interaction context. + * + * + * Contexts can be shared and contain multiple viewers, this method will + * always return the player for the current event. + * + * @return A player in this interaction context. + */ + val player: Player + + @get:Experimental + val allPlayers: List + + /** + * Updates the container title for a specific player. + * + * + * This should not be used before the container is opened, if you need to set the __initial + * title__ use [IFOpenContext.modifyConfig] on open handler instead. + * + * + * This method is version dependant, so it may be that your server version is not yet + * supported, if you try to use this method and fail (can fail silently), report it to the + * library developers to add support to your version. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param title The new container title. + * @param player The player to update the title. + */ + @Experimental + fun updateTitleForPlayer(title: Component, player: Player) + + /** + * Resets the container title only for the player current scope of execution to the initially + * defined title. Must be used after [.updateTitleForPlayer] to take effect. + * + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + * + * @param player The player to reset the title. + */ + @Experimental + fun resetTitleForPlayer(player: Player) +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt new file mode 100644 index 000000000..5895b1121 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt @@ -0,0 +1,141 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.* +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player +import java.util.* +import java.util.concurrent.CompletableFuture + +/** + * Creates a new open context instance. + * + * + * *** This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. *** + * + * @param root Root view that will be owner of the upcoming render context. + * @param subject The viewer that is opening the view. + * @param viewers Who'll be the viewers of this context, if this parameter is provided it + * means that this context is a shared context. + * Must be provided even in non-shared context cases. + * @param initialData Initial data provided by the user. + */ +class OpenContext constructor( + private val root: View, + private val subject: Viewer?, + private val viewers: Map, + initialData: Any +) : PlatformConfinedContext(), IFOpenContext, Context { + private var container: ViewContainer? = null + + // --- Inherited --- + private val id: UUID = UUID.randomUUID() + private var initialData: Any + + // --- User Provided --- + private var waitTask: CompletableFuture? = null + private var inheritedConfigBuilder: ViewConfigBuilder? = null + + // --- Properties --- + /** + * The player that's currently opening the view. + * + * @return The player that is opening the view. + * @throws UnsupportedOperationInSharedContextException If this context [is shared][.isShared]. + */ + override val player: Player + get() { + tryThrowDoNotWorkWithSharedContext("getAllPlayers()") + return field + } + private var cancelled = false + + init { + this.initialData = initialData + this.player = (subject as MinestomViewer).player + } + + override val allPlayers: List + get() = getViewers().stream() + .map { viewer -> (viewer as MinestomViewer).player } + .toList(); + override fun updateTitleForPlayer(title: Component, player: Player) { + tryThrowDoNotWorkWithSharedContext() + modifyConfig().title(title) + } + + override fun resetTitleForPlayer(player: Player) { + tryThrowDoNotWorkWithSharedContext() + if (modifiedConfig == null) return + + modifyConfig().title(null) + } + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + } + + override fun getAsyncOpenJob(): CompletableFuture { + return waitTask!! + } + + override fun getRoot(): View { + return root + } + + override fun getIndexedViewers(): Map { + return viewers + } + + override fun getId(): UUID { + return id + } + + override fun getInitialData(): Any { + return initialData + } + + override fun setInitialData(initialData: Any) { + this.initialData = initialData + } + + override fun waitUntil(task: CompletableFuture) { + this.waitTask = task + } + + override fun getConfig(): ViewConfig { + return if (inheritedConfigBuilder == null) + getRoot().config + else + Objects.requireNonNull(modifiedConfig, "Modified config cannot be null") + } + + override fun getModifiedConfig(): ViewConfig? { + if (inheritedConfigBuilder == null) return null + + return inheritedConfigBuilder!!.build().merge(getRoot().config) + } + + override fun modifyConfig(): ViewConfigBuilder { + if (inheritedConfigBuilder == null) inheritedConfigBuilder = ViewConfigBuilder() + + return inheritedConfigBuilder!! + } + + override fun getViewer(): Viewer? { + tryThrowDoNotWorkWithSharedContext("getViewers()") + return subject + } + + override fun getContainer(): ViewContainer? { + return container + } + + override fun setContainer(container: ViewContainer) { + this.container = container + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/RenderContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/RenderContext.kt new file mode 100644 index 000000000..c3d0f8ace --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/RenderContext.kt @@ -0,0 +1,149 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.MinestomViewContainer +import me.devnatan.inventoryframework.MinestomViewer +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfig +import me.devnatan.inventoryframework.ViewContainer +import me.devnatan.inventoryframework.Viewer +import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player +import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.ApiStatus +import java.util.* + +class RenderContext @ApiStatus.Internal constructor( + id: UUID, + root: View, + config: ViewConfig, + container: ViewContainer, + viewers: Map, + subject: Viewer, + initialData: Any? +) : + PlatformRenderContext( + id, + root, + config, + container, + viewers, + subject, + initialData + ), + Context { + override val player: Player = (subject as MinestomViewer).player + get() { + tryThrowDoNotWorkWithSharedContext("getAllPlayers") + return field + } + + override fun getRoot(): View { + return root as View + } + + override val allPlayers: List + get() = viewers.stream() + .map { viewer -> (viewer as MinestomViewer).player }.toList() + + + override fun updateTitleForPlayer(title: Component, player: Player) { + (container as MinestomViewContainer).changeTitle(title, player) + } + + override fun resetTitleForPlayer(player: Player) { + (container as MinestomViewContainer).changeTitle(Component.empty(), player) + } + + /** + * Adds an item to a specific slot in the context container. + * + * @param slot The slot in which the item will be positioned. + * @return An item builder to configure the item. + */ + fun slot(slot: Int, item: ItemStack): MinestomIemComponentBuilder { + return slot(slot).withItem(item) + } + + /** + * Adds an item at the specific column and ROW (X, Y) in that context's container. + * + * @param row The row (Y) in which the item will be positioned. + * @param column The column (X) in which the item will be positioned. + * @return An item builder to configure the item. + */ + fun slot(row: Int, column: Int, item: ItemStack?): MinestomIemComponentBuilder { + return slot(row, column).withItem(item) + } + + /** + * Sets an item in the first slot of this context's container. + * + * @param item The item that'll be set. + * @return An item builder to configure the item. + */ + fun firstSlot(item: ItemStack?): MinestomIemComponentBuilder { + return firstSlot().withItem(item) + } + + /** + * Sets an item in the last slot of this context's container. + * + * @param item The item that'll be set. + * @return An item builder to configure the item. + */ + fun lastSlot(item: ItemStack?): MinestomIemComponentBuilder { + return lastSlot().withItem(item) + } + + /** + * Adds an item in the next available slot of this context's container. + * + * @param item The item that'll be added. + * @return An item builder to configure the item. + */ + fun availableSlot(item: ItemStack?): MinestomIemComponentBuilder { + return availableSlot().withItem(item) + } + + /** + * Defines the item that will represent a character provided in the context layout. + * + * @param character The layout character target. + * @param item The item that'll represent the layout character. + * @return An item builder to configure the item. + */ + fun layoutSlot(character: Char, item: ItemStack?): MinestomIemComponentBuilder { + return layoutSlot(character).withItem(item) + } + + /** + * + * *** This API is experimental and is not subject to the general compatibility guarantees + * such API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + fun resultSlot(item: ItemStack?): MinestomIemComponentBuilder { + return resultSlot().withItem(item) + } + + override fun createBuilder(): MinestomIemComponentBuilder { + return MinestomIemComponentBuilder(this) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + if (!super.equals(other)) return false + val that = other as RenderContext + return player== that.player + } + + override fun hashCode(): Int { + return Objects.hash(super.hashCode(), player) + } + + override fun toString(): String { + return "RenderContext{" + "player=" + player + "} " + super.toString() + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt new file mode 100644 index 000000000..335ecf57d --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt @@ -0,0 +1,121 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.RootView +import me.devnatan.inventoryframework.ViewContainer +import me.devnatan.inventoryframework.Viewer +import me.devnatan.inventoryframework.component.Component +import net.minestom.server.entity.Player +import net.minestom.server.event.inventory.InventoryPreClickEvent +import net.minestom.server.inventory.PlayerInventory +import net.minestom.server.inventory.click.ClickType +import net.minestom.server.item.ItemStack + +class SlotClickContext constructor( + slot: Int, + parent: IFRenderContext, + private val whoClicked: Viewer, + private val clickedContainer: ViewContainer, + private val clickedComponent: Component?, + val clickOrigin: InventoryPreClickEvent, + private val combined: Boolean +): SlotContext(slot, parent), IFSlotClickContext { + private var cancelled = false + + override val player: Player + /** + * The player who clicked on the slot. + */ + get() = clickOrigin.player + + override val item: ItemStack + /** + * The item that was clicked. + */ + get() = clickOrigin.cursorItem + + override fun getComponent(): Component? { + return clickedComponent + } + + override fun getClickedContainer(): ViewContainer { + return clickedContainer + } + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + clickOrigin.isCancelled = cancelled + } + + override fun getPlatformEvent(): Any { + return clickOrigin + } + + override fun getClickedSlot(): Int { + return clickOrigin.slot + } + + override fun isLeftClick(): Boolean { + return clickOrigin.clickType == ClickType.LEFT_CLICK + } + + override fun isRightClick(): Boolean { + return clickOrigin.clickType == ClickType.RIGHT_CLICK + } + + override fun isMiddleClick(): Boolean { + return clickOrigin.clickType == ClickType.CHANGE_HELD + } + + override fun isShiftClick(): Boolean { + val clickType = clickOrigin.clickType + return clickType == ClickType.SHIFT_CLICK + } + + override fun isKeyboardClick(): Boolean { + return clickOrigin.clickType == ClickType.CHANGE_HELD + } + + override fun isOutsideClick(): Boolean { + return clickOrigin.inventory == null; + } + + override fun getClickIdentifier(): String { + return clickOrigin.clickType.name + } + + override fun isOnEntityContainer(): Boolean { + return clickOrigin.inventory is PlayerInventory + } + + override fun getViewer(): Viewer { + return whoClicked + } + + override fun closeForPlayer() { + parent.closeForPlayer() + } + + override fun openForPlayer(other: Class) { + parent.openForPlayer(other) + } + + override fun openForPlayer(other: Class, initialData: Any) { + parent.openForPlayer(other, initialData) + } + + override fun updateTitleForPlayer(title: String) { + parent.updateTitleForPlayer(title) + } + + override fun resetTitleForPlayer() { + parent.resetTitleForPlayer() + } + + override fun isCombined(): Boolean { + return combined + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt new file mode 100644 index 000000000..97cb41ce0 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt @@ -0,0 +1,172 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfig +import me.devnatan.inventoryframework.ViewContainer +import me.devnatan.inventoryframework.Viewer +import me.devnatan.inventoryframework.component.Component +import me.devnatan.inventoryframework.state.State +import me.devnatan.inventoryframework.state.StateValue +import me.devnatan.inventoryframework.state.StateWatcher +import net.minestom.server.entity.Player +import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.UnmodifiableView +import java.util.* + +abstract class SlotContext protected constructor( + private var slot: Int, + private val parent: IFRenderContext +) : PlatformContext(), + IFSlotContext, Context { + abstract val item: ItemStack + + override fun getParent(): RenderContext { + return parent as RenderContext + } + + override fun getSlot(): Int { + return slot + } + + override fun setSlot(slot: Int) { + this.slot = slot + } + + override fun getIndexedViewers(): Map { + return getParent().indexedViewers + } + + override fun getTitle(): String { + return getParent().title + } + + override fun getComponents(): @UnmodifiableView MutableList { + return getParent().components + } + + override fun getInternalComponents(): List { + return getParent().internalComponents + } + + override fun getComponentsAt(position: Int): List { + return getParent().getComponentsAt(position) + } + + override fun addComponent(component: Component) { + getParent().addComponent(component) + } + + override fun removeComponent(component: Component) { + getParent().removeComponent(component) + } + + override fun renderComponent(component: Component) { + getParent().renderComponent(component) + } + + override fun updateComponent(component: Component, force: Boolean) { + getParent().updateComponent(component, force) + } + + override fun performClickInComponent( + component: Component, + viewer: Viewer, + clickedContainer: ViewContainer, + platformEvent: Any, + clickedSlot: Int, + combined: Boolean + ) { + getParent().performClickInComponent(component, viewer, clickedContainer, platformEvent, clickedSlot, combined) + } + + override fun update() { + getParent().update() + } + + override fun getRawStateValue(state: State<*>?): Any { + return getParent().getRawStateValue(state) + } + + override fun getInternalStateValue(state: State<*>): StateValue { + return getParent().getInternalStateValue(state) + } + + override fun getUninitializedStateValue(stateId: Long): StateValue { + return getParent().getUninitializedStateValue(stateId) + } + + override fun initializeState(id: Long, value: StateValue) { + getParent().initializeState(id, value) + } + + override fun updateState(id: Long, value: Any) { + getParent().updateState(id, value) + } + + override fun watchState(id: Long, listener: StateWatcher) { + getParent().watchState(id, listener) + } + + override fun getId(): UUID { + return getParent().id + } + + override fun getConfig(): ViewConfig { + return getParent().config + } + + override fun getContainer(): ViewContainer { + return getParent().container + } + + override fun getRoot(): View { + return getParent().getRoot() + } + + override fun getInitialData(): Any { + return getParent().initialData + } + + override fun setInitialData(initialData: Any) { + getParent().initialData = initialData + } + + override val allPlayers: List + get() = getParent().allPlayers + + override fun updateTitleForPlayer(title: net.kyori.adventure.text.Component, player: Player) { + getParent().updateTitleForPlayer(title, player) + } + + override fun resetTitleForPlayer(player: Player) { + getParent().resetTitleForPlayer(player) + } + + override fun isActive(): Boolean { + return getParent().isActive + } + + override fun setActive(active: Boolean) { + getParent().isActive = active + } + + override fun isEndless(): Boolean { + return getParent().isEndless + } + + override fun setEndless(endless: Boolean) { + getParent().isEndless = endless + } + + override fun back() { + getParent().back() + } + + override fun back(initialData: Any) { + getParent().back(initialData) + } + + override fun canBack(): Boolean { + return getParent().canBack() + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt new file mode 100644 index 000000000..c690d6e7a --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt @@ -0,0 +1,78 @@ +package me.devnatan.inventoryframework.context + +import me.devnatan.inventoryframework.MinestomViewer +import me.devnatan.inventoryframework.RootView +import me.devnatan.inventoryframework.Viewer +import net.minestom.server.entity.Player +import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.UnknownNullability + +class SlotRenderContext constructor(slot: Int, parent: IFRenderContext, private val viewer: Viewer?) : + SlotContext(slot, parent), IFSlotRenderContext { + override val player: Player = (viewer as MinestomViewer).player + + override var item: ItemStack = ItemStack.AIR + private var cancelled = false + private var changed = false + private var forceUpdate = false + + override fun getResult(): ItemStack { + return item + } + + override fun isCancelled(): Boolean { + return cancelled + } + + override fun setCancelled(cancelled: Boolean) { + this.cancelled = cancelled + } + + override fun clear() { + item = ItemStack.AIR + } + + override fun hasChanged(): Boolean { + return changed + } + + override fun setChanged(changed: Boolean) { + this.changed = changed + } + + override fun isForceUpdate(): Boolean { + return forceUpdate + } + + override fun setForceUpdate(forceUpdate: Boolean) { + this.forceUpdate = forceUpdate + } + + override fun isOnEntityContainer(): Boolean { + return container.isEntityContainer + } + + override fun getViewer(): Viewer? { + return viewer + } + + override fun closeForPlayer() { + parent.closeForPlayer() + } + + override fun openForPlayer(other: Class) { + parent.openForPlayer(other) + } + + override fun openForPlayer(other: Class, initialData: Any) { + parent.openForPlayer(other, initialData) + } + + override fun updateTitleForPlayer(title: String) { + parent.updateTitleForPlayer(title) + } + + override fun resetTitleForPlayer() { + parent.resetTitleForPlayer() + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt new file mode 100644 index 000000000..b29d3d3e9 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt @@ -0,0 +1,155 @@ +package me.devnatan.inventoryframework.internal + +import me.devnatan.inventoryframework.* +import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder +import me.devnatan.inventoryframework.component.Component +import me.devnatan.inventoryframework.component.ComponentBuilder +import me.devnatan.inventoryframework.context.* +import me.devnatan.inventoryframework.logging.Logger +import me.devnatan.inventoryframework.logging.NoopLogger +import net.minestom.server.entity.Player +import net.minestom.server.event.inventory.InventoryPreClickEvent +import net.minestom.server.inventory.Inventory +import net.minestom.server.inventory.InventoryType +import java.util.* +import java.util.function.Function +import java.util.stream.Collectors + +class MinestomElementFactory : ElementFactory() { + private var worksInCurrentPlatform: Boolean? = null + + override fun createUninitializedRoot(): RootView { + return View() + } + + // TODO Test it + override fun createContainer(context: IFContext): ViewContainer { + val config: ViewConfig = context.getConfig() + val finalType: ViewType = config.type ?: defaultType + + val size: Int = finalType.normalize(config.getSize()) + require(!(size != 0 && !finalType.isExtendable())) { + String.format( + ("Only \"%s\" type can have a custom size," + + " \"%s\" always have a size of %d. Remove the parameter that specifies the size" + + " of the container on %s or just set the type explicitly."), + ViewType.CHEST.getIdentifier(), + finalType.getIdentifier(), + finalType.getMaxSize(), + context.getRoot().javaClass.getName() + ) + } + + + val type = when (finalType) { + ViewType.CHEST -> { + when (size / finalType.columns) { + 1 -> InventoryType.CHEST_1_ROW + 2 -> InventoryType.CHEST_2_ROW + 3 -> InventoryType.CHEST_3_ROW + 4 -> InventoryType.CHEST_4_ROW + 5 -> InventoryType.CHEST_5_ROW + 6 -> InventoryType.CHEST_6_ROW + else -> InventoryType.CHEST_6_ROW + } + } + ViewType.BEACON -> InventoryType.BEACON + ViewType.HOPPER -> InventoryType.HOPPER + ViewType.SMOKER -> InventoryType.SMOKER + ViewType.BLAST_FURNACE -> InventoryType.BLAST_FURNACE + ViewType.FURNACE -> InventoryType.FURNACE + ViewType.ANVIL -> InventoryType.ANVIL + ViewType.CRAFTING_TABLE -> InventoryType.CRAFTING + ViewType.DROPPER, ViewType.DROPPER -> InventoryType.WINDOW_3X3 + ViewType.BREWING_STAND -> InventoryType.BREWING_STAND + ViewType.SHULKER_BOX -> InventoryType.SHULKER_BOX + else -> error("Unsupported type: $finalType") + } + + val inventory = Inventory(type, net.kyori.adventure.text.Component.empty()) + return MinestomViewContainer(inventory, false, finalType, false) + } + + override fun createViewer(entity: Any, context: IFRenderContext): Viewer { + require(entity is Player) { "createViewer(...) first parameter must be a Player" } + + return MinestomViewer(entity as Player, context) + } + + override fun createOpenContext( + root: RootView, subject: Viewer?, viewers: List, initialData: Any + ): IFOpenContext { + return OpenContext( + root as View, + subject, + viewers.stream().collect( + Collectors.toMap( + Function { obj: Viewer -> obj.getId() }, Function.identity() + ) + ), + initialData + ) + } + + override fun createRenderContext( + id: UUID, + root: RootView, + config: ViewConfig, + container: ViewContainer, + viewers: Map, + subject: Viewer, + initialData: Any + ): IFRenderContext { + return RenderContext(id, root as View, config, container, viewers, subject, initialData) + } + + override fun createSlotClickContext( + slotClicked: Int, + whoClicked: Viewer, + interactionContainer: ViewContainer, + componentClicked: Component?, + origin: Any, + combined: Boolean + ): IFSlotClickContext { + val context: IFRenderContext = whoClicked.getActiveContext() + return SlotClickContext( + slotClicked, + context, + whoClicked, + interactionContainer, + componentClicked, + origin as InventoryPreClickEvent, + combined + ) + } + + override fun createSlotRenderContext( + slot: Int, parent: IFRenderContext, viewer: Viewer? + ): IFSlotRenderContext { + return SlotRenderContext(slot, parent, viewer) + } + + override fun createCloseContext(viewer: Viewer, parent: IFRenderContext): IFCloseContext { + return CloseContext(viewer, parent) + } + + override fun createComponentBuilder(root: VirtualView): ComponentBuilder<*, Context> { + return MinestomIemComponentBuilder(root) + } + + override fun worksInCurrentPlatform(): Boolean { + return true + } + + override fun getLogger(): Logger { + return NoopLogger() + } + + override fun scheduleJobInterval(root: RootView, intervalInTicks: Long, execution: Runnable): Job { + return MinestomTaskJobImpl(intervalInTicks.toInt(), execution) + } + + companion object { + private val defaultType: ViewType = ViewType.CHEST + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomTaskJobImpl.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomTaskJobImpl.kt new file mode 100644 index 000000000..7afaa6859 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomTaskJobImpl.kt @@ -0,0 +1,31 @@ +package me.devnatan.inventoryframework.internal + +import net.minestom.server.MinecraftServer +import net.minestom.server.timer.Task +import net.minestom.server.timer.TaskSchedule + +internal class MinestomTaskJobImpl(private val intervalInTicks: Int, + private val execution: Runnable +) : Job { + private var task: Task? = null + + override fun isStarted(): Boolean { + return task != null + } + + override fun start() { + if (isStarted) return + val schedule = TaskSchedule.tick(intervalInTicks) + task = MinecraftServer.getSchedulerManager().scheduleTask( this::loop, schedule, schedule) + } + + override fun cancel() { + if (!isStarted) return + task?.cancel() + task = null + } + + override fun loop() { + execution.run() + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt new file mode 100644 index 000000000..fa0d545e5 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt @@ -0,0 +1,29 @@ +package me.devnatan.inventoryframework.logging + +class BukkitLogger( + private val logger: java.util.logging.Logger, + private val viewName: String, + private val isShaded: Boolean +) : Logger { + private var finalPrefix: String? = null + + override fun getPrefix(): String? { + if (finalPrefix == null) { + finalPrefix = String.format("%s[%s]", if (isShaded) "[IF]" else "", viewName) + " " + } + + return finalPrefix + } + + override fun debug(message: String) { + logger.info(prefix + message) + } + + override fun warn(message: String) { + logger.warning(prefix + message) + } + + override fun error(message: String) { + logger.severe(prefix + message) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt new file mode 100644 index 000000000..91ee48d65 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt @@ -0,0 +1,15 @@ +package me.devnatan.inventoryframework.pipeline + +import me.devnatan.inventoryframework.VirtualView +import me.devnatan.inventoryframework.context.CloseContext + + +class CancelledCloseInterceptor : PipelineInterceptor { + override fun intercept(pipeline: PipelineContext, context: VirtualView) { + if (context !is CloseContext) return + + if (!context.isCancelled) return + + context.root.nextTick { context.viewer.open(context.container) } + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt new file mode 100644 index 000000000..d65e52bcf --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt @@ -0,0 +1,24 @@ +package me.devnatan.inventoryframework.pipeline + +import me.devnatan.inventoryframework.ViewConfig +import me.devnatan.inventoryframework.VirtualView +import me.devnatan.inventoryframework.context.SlotClickContext +import net.minestom.server.event.inventory.InventoryPreClickEvent + +/** + * Intercepted when a player clicks on the view container. + * If the click is canceled, this interceptor ends the pipeline immediately. + */ +class GlobalClickInterceptor : PipelineInterceptor { + override fun intercept(pipeline: PipelineContext, subject: VirtualView) { + if (subject !is SlotClickContext) return + + val context = subject as SlotClickContext + val event: InventoryPreClickEvent = context.clickOrigin + + // inherit cancellation so we can un-cancel it + context.isCancelled = + event.isCancelled || context.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true) + context.root.onClick(context) + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt new file mode 100644 index 000000000..e7033a35a --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt @@ -0,0 +1,28 @@ +package me.devnatan.inventoryframework.pipeline + +import me.devnatan.inventoryframework.VirtualView +import me.devnatan.inventoryframework.component.ItemComponent +import me.devnatan.inventoryframework.context.SlotClickContext +import net.minestom.server.event.inventory.InventoryPreClickEvent + +/** + * Intercepted when a player clicks on an item the view container. + */ +class ItemClickInterceptor : PipelineInterceptor { + override fun intercept(pipeline: PipelineContext, subject: VirtualView) { + if (subject !is SlotClickContext) return + + val context = subject as SlotClickContext + val event: InventoryPreClickEvent = context.clickOrigin + event.inventory ?: return + + val component = context.component ?: return + + if (component is ItemComponent) { + val item: ItemComponent = component + + // inherit cancellation so we can un-cancel it + context.isCancelled = item.isCancelOnClick + } + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemCloseOnClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemCloseOnClickInterceptor.kt new file mode 100644 index 000000000..d1d7b3463 --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemCloseOnClickInterceptor.kt @@ -0,0 +1,28 @@ +package me.devnatan.inventoryframework.pipeline + +import me.devnatan.inventoryframework.VirtualView +import me.devnatan.inventoryframework.component.ItemComponent +import me.devnatan.inventoryframework.context.SlotClickContext +import net.minestom.server.event.inventory.InventoryPreClickEvent + +/** + * Intercepted when a player clicks on an item the view container. Checks if the container should be + * closed when the item is clicked. + */ +class ItemCloseOnClickInterceptor : PipelineInterceptor { + override fun intercept(pipeline: PipelineContext, subject: VirtualView) { + if (subject !is SlotClickContext) return + + val event: InventoryPreClickEvent = subject.clickOrigin + event.inventory ?: return + + val component = subject.component + if (component !is ItemComponent || !component.isVisible) return + + val item: ItemComponent = component as ItemComponent + if (item.isCloseOnClick) { + subject.closeForPlayer() + pipeline.finish() + } + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt new file mode 100644 index 000000000..85239214b --- /dev/null +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt @@ -0,0 +1,7 @@ +package me.devnatan.inventoryframework.util + +object IsTypeOf { + fun isTypeOf(superCls: Class<*>, cls: Class<*>): Boolean { + return superCls.isAssignableFrom(cls) + } +} diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java index e93e47e9e..9808ffc15 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -abstract class IFViewFrame, V extends PlatformView> { +public abstract class IFViewFrame, V extends PlatformView> { /** * Called when a {@link IFViewFrame} is registered. diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java index f18c65033..a716987e4 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java @@ -65,6 +65,10 @@ public abstract class PlatformView< private final StateAccess stateAccess = new StateAccessImpl<>(this, getElementFactory(), stateRegistry); + protected PlatformView() { + super(); + } + // region Open & Close /** * Closes all contexts that are currently active in this view. diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java index 09d0b0347..84fe802bc 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java @@ -9,7 +9,9 @@ import me.devnatan.inventoryframework.Viewer; import org.jetbrains.annotations.NotNull; -abstract class PlatformConfinedContext extends PlatformContext implements IFConfinedContext { +public abstract class PlatformConfinedContext extends PlatformContext implements IFConfinedContext { + + protected PlatformConfinedContext() {} @Override public abstract Viewer getViewer(); diff --git a/settings.gradle b/settings.gradle index 71e5d0554..627da57cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ buildscript { maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://repo.papermc.io/repository/maven-public/' } + maven { url 'https://jitpack.io' } } } } @@ -22,6 +23,7 @@ include 'inventory-framework-test', 'inventory-framework-platform', 'inventory-framework-platform-paper', 'inventory-framework-platform-bukkit', + 'inventory-framework-platform-minestom', 'inventory-framework-anvil-input', 'example' From 2690b9f4617efc2248621436722b0da1b21e158b Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 09:41:04 +0100 Subject: [PATCH 02/15] Move paper example in subdir --- {example => examples/paper}/.gitignore | 0 {example => examples/paper}/build.gradle | 0 .../me/devnatan/inventoryframework/runtime/ExampleUtil.java | 0 .../me/devnatan/inventoryframework/runtime/SamplePlugin.java | 0 .../runtime/commands/IFExampleCommandExecutor.java | 0 .../inventoryframework/runtime/view/AnvilInputSample.java | 0 .../me/devnatan/inventoryframework/runtime/view/Failing.java | 0 .../inventoryframework/runtime/view/SimplePagination.java | 0 settings.gradle | 3 ++- 9 files changed, 2 insertions(+), 1 deletion(-) rename {example => examples/paper}/.gitignore (100%) rename {example => examples/paper}/build.gradle (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/ExampleUtil.java (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/SamplePlugin.java (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/commands/IFExampleCommandExecutor.java (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/view/AnvilInputSample.java (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/view/Failing.java (100%) rename {example => examples/paper}/src/main/java/me/devnatan/inventoryframework/runtime/view/SimplePagination.java (100%) diff --git a/example/.gitignore b/examples/paper/.gitignore similarity index 100% rename from example/.gitignore rename to examples/paper/.gitignore diff --git a/example/build.gradle b/examples/paper/build.gradle similarity index 100% rename from example/build.gradle rename to examples/paper/build.gradle diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/ExampleUtil.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/ExampleUtil.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/ExampleUtil.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/ExampleUtil.java diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/SamplePlugin.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/SamplePlugin.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/SamplePlugin.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/SamplePlugin.java diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/commands/IFExampleCommandExecutor.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/commands/IFExampleCommandExecutor.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/commands/IFExampleCommandExecutor.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/commands/IFExampleCommandExecutor.java diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/view/AnvilInputSample.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/AnvilInputSample.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/view/AnvilInputSample.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/AnvilInputSample.java diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/view/Failing.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/Failing.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/view/Failing.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/Failing.java diff --git a/example/src/main/java/me/devnatan/inventoryframework/runtime/view/SimplePagination.java b/examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/SimplePagination.java similarity index 100% rename from example/src/main/java/me/devnatan/inventoryframework/runtime/view/SimplePagination.java rename to examples/paper/src/main/java/me/devnatan/inventoryframework/runtime/view/SimplePagination.java diff --git a/settings.gradle b/settings.gradle index 627da57cc..3aa753c39 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,5 +25,6 @@ include 'inventory-framework-test', 'inventory-framework-platform-bukkit', 'inventory-framework-platform-minestom', 'inventory-framework-anvil-input', - 'example' + 'example-bukkit' +project(':example-bukkit').projectDir = new File('examples/bukkit') From 50dceb336e180406ae4c9415159709893db233ec Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 09:41:07 +0100 Subject: [PATCH 03/15] Move paper example in subdir --- examples/paper/build.gradle | 2 +- settings.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/paper/build.gradle b/examples/paper/build.gradle index b3a45b89f..2098aeff1 100644 --- a/examples/paper/build.gradle +++ b/examples/paper/build.gradle @@ -5,7 +5,7 @@ plugins { alias(libs.plugins.run.paper) } -apply from: '../library.gradle' +apply from: '../../library.gradle' dependencies { implementation projects.inventoryFrameworkPlatformBukkit diff --git a/settings.gradle b/settings.gradle index 3aa753c39..b07d7ed47 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,6 @@ include 'inventory-framework-test', 'inventory-framework-platform-bukkit', 'inventory-framework-platform-minestom', 'inventory-framework-anvil-input', - 'example-bukkit' + 'example-paper' -project(':example-bukkit').projectDir = new File('examples/bukkit') +project(':example-paper').projectDir = new File('examples/paper') From 2a3c73de527f812f9cd681cedf7d81ec9f6f462c Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 09:43:52 +0100 Subject: [PATCH 04/15] Remove Anvil Input from minestoime since it will never work --- inventory-framework-platform-minestom/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/inventory-framework-platform-minestom/build.gradle b/inventory-framework-platform-minestom/build.gradle index cc7e7bcf6..4602460bd 100644 --- a/inventory-framework-platform-minestom/build.gradle +++ b/inventory-framework-platform-minestom/build.gradle @@ -10,7 +10,6 @@ apply from: '../publish.gradle' dependencies { api projects.inventoryFrameworkPlatform - runtimeOnly projects.inventoryFrameworkAnvilInput compileOnly libs.minestom testCompileOnly libs.minestom testImplementation projects.inventoryFrameworkApi @@ -23,7 +22,6 @@ shadowJar { dependencies { include(project(":inventory-framework-platform")) - include(project(":inventory-framework-anvil-input")) } } From ea3c034b88bcc855328acaf58cf5eca728504e52 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 11:18:35 +0100 Subject: [PATCH 05/15] Add sample server & get to first working prototype --- ...kotlin-compiler-9388713202489506517.salive | 0 examples/minestom/build.gradle | 29 +++++++++++ .../inventoryframework/runtime/ExampleUtil.kt | 28 ++++++++++ .../runtime/SampleServer.kt | 52 +++++++++++++++++++ .../runtime/command/IFExampleCommand.kt | 52 +++++++++++++++++++ .../runtime/view/Failing.kt | 44 ++++++++++++++++ .../runtime/view/SimplePagination.kt | 50 ++++++++++++++++++ .../inventoryframework/MinestomViewer.kt | 10 ++-- .../me/devnatan/inventoryframework/View.kt | 2 +- .../devnatan/inventoryframework/ViewFrame.kt | 1 + .../inventoryframework/context/OpenContext.kt | 11 ++-- .../internal/MinestomElementFactory.kt | 8 +-- .../inventoryframework/util/IsTypeOf.kt | 7 --- settings.gradle | 4 +- 14 files changed, 272 insertions(+), 26 deletions(-) create mode 100644 .kotlin/sessions/kotlin-compiler-9388713202489506517.salive create mode 100644 examples/minestom/build.gradle create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt delete mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt diff --git a/.kotlin/sessions/kotlin-compiler-9388713202489506517.salive b/.kotlin/sessions/kotlin-compiler-9388713202489506517.salive new file mode 100644 index 000000000..e69de29bb diff --git a/examples/minestom/build.gradle b/examples/minestom/build.gradle new file mode 100644 index 000000000..979f58de5 --- /dev/null +++ b/examples/minestom/build.gradle @@ -0,0 +1,29 @@ + +plugins { + alias(libs.plugins.shadowjar) + alias(libs.plugins.kotlin) + id("application") +} + +apply from: '../../library.gradle' + +dependencies { + implementation projects.inventoryFrameworkPlatformMinestom + implementation libs.minestom +} + +shadowJar { + archiveBaseName.set('inventory-framework-example') + archiveAppendix.set('minestome') +} + +java { + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + +application { + mainClass = 'me.devnatan.inventoryframework.runtime.SampleServerKt' +} \ No newline at end of file diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt new file mode 100644 index 000000000..da612f191 --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt @@ -0,0 +1,28 @@ +package me.devnatan.inventoryframework.runtime + +import net.kyori.adventure.text.Component +import net.minestom.server.item.ItemStack +import net.minestom.server.item.Material +import java.util.* + + +object ExampleUtil { + @JvmStatic + fun getRandomItems(amount: Int): List { + val materials = Material.values().toTypedArray() + val random = Random() + + val result = ArrayList() + + for (i in 0 until amount) { + result.add(ItemStack.of(materials[random.nextInt(10, 100)])) + } + + return result + } + + @JvmStatic + fun displayItem(material: Material, displayName: String): ItemStack { + return ItemStack.of(material).withCustomName(Component.text(displayName)) + } +} diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt new file mode 100644 index 000000000..f8d89ccc0 --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt @@ -0,0 +1,52 @@ +package me.devnatan.inventoryframework.runtime + +import me.devnatan.inventoryframework.ViewFrame +import me.devnatan.inventoryframework.runtime.command.IFExampleCommand +import me.devnatan.inventoryframework.runtime.view.Failing +import me.devnatan.inventoryframework.runtime.view.SimplePagination +import net.minestom.server.MinecraftServer +import net.minestom.server.coordinate.Pos +import net.minestom.server.entity.PlayerSkin +import net.minestom.server.event.player.AsyncPlayerConfigurationEvent +import net.minestom.server.event.player.PlayerSkinInitEvent +import net.minestom.server.instance.LightingChunk +import net.minestom.server.instance.block.Block + +class SampleServer { + + init { + val server = MinecraftServer.init() + val instanceManager = MinecraftServer.getInstanceManager() + MinecraftServer.getCommandManager().register() + + // Create word filled with quartz blocks up to height 50 + val instance = instanceManager.createInstanceContainer() + instance.setGenerator { + it.modifier().fillHeight(it.absoluteStart().blockY(), 50, Block.QUARTZ_BLOCK) + } + instance.setChunkSupplier(::LightingChunk) + + val handler = MinecraftServer.getGlobalEventHandler() + + handler.addListener(AsyncPlayerConfigurationEvent::class.java) { event -> + event.spawningInstance = instance + event.player.respawnPoint = Pos(0.0, 53.0, 0.0) + } + handler.addListener(PlayerSkinInitEvent::class.java) { event -> + event.skin = PlayerSkin.fromUsername(event.player.username) + } + + val viewFrame = ViewFrame.create() + .with(Failing(), SimplePagination()) + .register() + + MinecraftServer.getCommandManager().register(IFExampleCommand(viewFrame)) + + server.start("0.0.0.0", 25565) + } + +} + +fun main() { + SampleServer() +} diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt new file mode 100644 index 000000000..b06a28e0b --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt @@ -0,0 +1,52 @@ +package me.devnatan.inventoryframework.runtime.command + +import me.devnatan.inventoryframework.ViewFrame +import me.devnatan.inventoryframework.runtime.view.Failing +import me.devnatan.inventoryframework.runtime.view.SimplePagination +import net.minestom.server.command.CommandSender +import net.minestom.server.command.builder.Command +import net.minestom.server.command.builder.CommandContext +import net.minestom.server.command.builder.arguments.Argument +import net.minestom.server.command.builder.arguments.ArgumentType +import net.minestom.server.command.builder.suggestion.SuggestionEntry +import net.minestom.server.entity.Player +import java.util.* + +class IFExampleCommand(private val viewFrame: ViewFrame) : Command("ifexample") { + + private val availableViews = mapOf( + "failing" to Failing::class.java, + "simple-pagination" to SimplePagination::class.java + ); + + private val arg: Argument = + ArgumentType.String("view").setSuggestionCallback { _, _, suggestion -> + availableViews.keys.forEach { + suggestion.addEntry(SuggestionEntry(it)) + } + } + + init { + addSyntax({ sender, ctx -> onCommand(sender, ctx) }, arg) + } + + private fun onCommand(sender: CommandSender, ctx: CommandContext) { + if (sender !is Player) { + sender.sendMessage("This command can only be executed by players.") + return + } + + val view = availableViews[ctx.get(arg)] + if (view != null) { + sender.sendMessage("Opened view: ${ctx.get(arg)}") + try { + viewFrame.open(view, sender) + } catch (e: Exception) { + e.printStackTrace() + } + } else { + sender.sendMessage("Unknown view: ${ctx.get(arg)}") + sender.sendMessage("Available views: ${availableViews.keys.joinToString(", ")}") + } + } +} \ No newline at end of file diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt new file mode 100644 index 000000000..f5ed79966 --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt @@ -0,0 +1,44 @@ +package me.devnatan.inventoryframework.runtime.view + +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfigBuilder +import me.devnatan.inventoryframework.context.RenderContext +import me.devnatan.inventoryframework.context.SlotClickContext +import me.devnatan.inventoryframework.context.SlotRenderContext +import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem +import me.devnatan.inventoryframework.state.MutableState +import net.minestom.server.item.Material + +class Failing : View() { + var state: MutableState = mutableState(0) + + override fun onInit(config: ViewConfigBuilder) { + config.size(1) + config.cancelOnClick() + config.title("Failing Inventory") + config.layout(" R C ") + } + + override fun onFirstRender(render: RenderContext) { + render.layoutSlot('R') + .onRender { ctx: SlotRenderContext -> + if (state[ctx] == 0) { + ctx.item = displayItem( + Material.DIAMOND, + "Click me to fail" + ) + } else { + throw IllegalStateException("This item cannot be rendered") + } + } + .onClick { ctx: SlotClickContext -> + state[1] = ctx + ctx.update() + } + + render.layoutSlot('C', displayItem(Material.STONE, "Click me and I will fail")) + .onClick { ctx: SlotClickContext? -> + throw IllegalStateException("This is a failing inventory") + } + } +} diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt new file mode 100644 index 000000000..bdf628dfe --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt @@ -0,0 +1,50 @@ +package me.devnatan.inventoryframework.runtime.view + +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfigBuilder +import me.devnatan.inventoryframework.component.MinestomIemComponentBuilder +import me.devnatan.inventoryframework.component.Pagination +import me.devnatan.inventoryframework.component.PaginationValueConsumer +import me.devnatan.inventoryframework.context.Context +import me.devnatan.inventoryframework.context.RenderContext +import me.devnatan.inventoryframework.context.SlotClickContext +import me.devnatan.inventoryframework.runtime.ExampleUtil.displayItem +import me.devnatan.inventoryframework.runtime.ExampleUtil.getRandomItems +import me.devnatan.inventoryframework.state.State +import net.minestom.server.item.ItemStack +import net.minestom.server.item.Material +import java.util.function.BooleanSupplier +import java.util.function.Supplier + +class SimplePagination : View() { + private val state: State = lazyPaginationState( + {_ -> getRandomItems(123).toMutableList() }, + { _: Context, builder: MinestomIemComponentBuilder, index: Int, value: ItemStack -> + builder.withItem(value) + builder.onClick { ctx: SlotClickContext -> + ctx.player.sendMessage( + "You clicked on item $index" + ) + } + }) + + override fun onInit(config: ViewConfigBuilder) { + config.cancelOnClick() + config.size(3) + config.title("Simple Pagination") + config.layout("OOOOOOOOO", "OOOOOOOOO", " P N ") + } + + override fun onFirstRender(render: RenderContext) { + val previousItem = displayItem(Material.ARROW, "Previous") + val nextItem = displayItem(Material.ARROW, "Next") + render.layoutSlot('P', previousItem) + .displayIf(BooleanSupplier { state[render].canBack() }) + .updateOnStateChange(state) + .onClick { ctx: SlotClickContext? -> state[ctx!!].back() } + render.layoutSlot('N', nextItem) + .displayIf(BooleanSupplier { state[render].canAdvance() }) + .updateOnStateChange(state) + .onClick { ctx: SlotClickContext? -> state[ctx!!].advance() } + } +} diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt index 666019c32..bd7d47133 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/MinestomViewer.kt @@ -5,19 +5,15 @@ import net.minestom.server.entity.Player import net.minestom.server.inventory.Inventory import java.util.* -class MinestomViewer(val player: Player, activeContext: IFRenderContext) : Viewer { +class MinestomViewer(val player: Player, private var activeContext: IFRenderContext?) : Viewer { private var selfContainer: ViewContainer? = null - private var activeContext: IFRenderContext private val previousContexts: Deque = LinkedList() private var lastInteractionInMillis: Long = 0 private var transitioning = false - init { - this.activeContext = activeContext - } override fun getActiveContext(): IFRenderContext { - return activeContext + return activeContext!! } override fun setActiveContext(context: IFRenderContext) { @@ -52,7 +48,7 @@ class MinestomViewer(val player: Player, activeContext: IFRenderContext) : Viewe } override fun isBlockedByInteractionDelay(): Boolean { - val configuredDelay: Long = activeContext.getConfig().getInteractionDelayInMillis() + val configuredDelay: Long = activeContext?.getConfig()?.interactionDelayInMillis ?: return false if (configuredDelay <= 0 || getLastInteractionInMillis() <= 0) return false return getLastInteractionInMillis() + configuredDelay >= System.currentTimeMillis() diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt index fe60f66b9..f9fea6122 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/View.kt @@ -11,7 +11,7 @@ import org.jetbrains.annotations.ApiStatus.OverrideOnly * Bukkit platform [PlatformView] implementation. */ @OverrideOnly -class View : +open class View : PlatformView() { public override fun registerPlatformInterceptors() { diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt index 6482bce80..d66f280db 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt @@ -166,6 +166,7 @@ class ViewFrame private constructor() : IFViewFrame() { check(!isRegistered) { "This view frame is already registered" } isRegistered = true + PlatformUtils.setFactory(MinestomElementFactory()) pipeline.execute(FRAME_REGISTERED, this) initializeViews() IFInventoryListener(this) diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt index 5895b1121..5f558576a 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt @@ -24,13 +24,12 @@ class OpenContext constructor( private val root: View, private val subject: Viewer?, private val viewers: Map, - initialData: Any + private var initialData: Any? ) : PlatformConfinedContext(), IFOpenContext, Context { private var container: ViewContainer? = null // --- Inherited --- private val id: UUID = UUID.randomUUID() - private var initialData: Any // --- User Provided --- private var waitTask: CompletableFuture? = null @@ -79,8 +78,8 @@ class OpenContext constructor( this.cancelled = cancelled } - override fun getAsyncOpenJob(): CompletableFuture { - return waitTask!! + override fun getAsyncOpenJob(): CompletableFuture? { + return waitTask } override fun getRoot(): View { @@ -95,11 +94,11 @@ class OpenContext constructor( return id } - override fun getInitialData(): Any { + override fun getInitialData(): Any? { return initialData } - override fun setInitialData(initialData: Any) { + override fun setInitialData(initialData: Any?) { this.initialData = initialData } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt index b29d3d3e9..03e9d2c3b 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt @@ -70,14 +70,14 @@ class MinestomElementFactory : ElementFactory() { return MinestomViewContainer(inventory, false, finalType, false) } - override fun createViewer(entity: Any, context: IFRenderContext): Viewer { + override fun createViewer(entity: Any, context: IFRenderContext?): Viewer { require(entity is Player) { "createViewer(...) first parameter must be a Player" } - return MinestomViewer(entity as Player, context) + return MinestomViewer(entity, context) } override fun createOpenContext( - root: RootView, subject: Viewer?, viewers: List, initialData: Any + root: RootView, subject: Viewer?, viewers: List, initialData: Any? ): IFOpenContext { return OpenContext( root as View, @@ -98,7 +98,7 @@ class MinestomElementFactory : ElementFactory() { container: ViewContainer, viewers: Map, subject: Viewer, - initialData: Any + initialData: Any? ): IFRenderContext { return RenderContext(id, root as View, config, container, viewers, subject, initialData) } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt deleted file mode 100644 index 85239214b..000000000 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/util/IsTypeOf.kt +++ /dev/null @@ -1,7 +0,0 @@ -package me.devnatan.inventoryframework.util - -object IsTypeOf { - fun isTypeOf(superCls: Class<*>, cls: Class<*>): Boolean { - return superCls.isAssignableFrom(cls) - } -} diff --git a/settings.gradle b/settings.gradle index b07d7ed47..6b175daaf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,8 @@ include 'inventory-framework-test', 'inventory-framework-platform-bukkit', 'inventory-framework-platform-minestom', 'inventory-framework-anvil-input', - 'example-paper' + 'example-paper', + 'example-minestom' project(':example-paper').projectDir = new File('examples/paper') +project(':example-minestom').projectDir = new File('examples/minestom') From 21a52ed67e8dd27da998a91649195eda06331a56 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 11:24:36 +0100 Subject: [PATCH 06/15] Try fixing CI --- .github/workflows/generate-artifacts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-artifacts.yml b/.github/workflows/generate-artifacts.yml index 93414fc17..65a1bd3ec 100644 --- a/.github/workflows/generate-artifacts.yml +++ b/.github/workflows/generate-artifacts.yml @@ -12,7 +12,7 @@ jobs: platform: [Bukkit, Paper] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: gradle/wrapper-validation-action@v1 - uses: actions/setup-java@v2 with: @@ -31,7 +31,7 @@ jobs: arguments: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}:shadowJar gradle-version: wrapper - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}-${{ github.sha }}.jar path: inventory-framework-platform-${{ steps.vars.outputs.lowercase }}/build/libs/*.jar From 5d4be7f668e56710307d1fe947ccc3a349ec2527 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 11:27:28 +0100 Subject: [PATCH 07/15] Add minestom artifact --- .github/workflows/generate-artifacts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-artifacts.yml b/.github/workflows/generate-artifacts.yml index 65a1bd3ec..e07b09ee4 100644 --- a/.github/workflows/generate-artifacts.yml +++ b/.github/workflows/generate-artifacts.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [Bukkit, Paper] + platform: [Bukkit, Paper, Minestom] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 39f4483d1b5c5a6df5fee1c54a3cd9e8041c17d5 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 22:58:48 +0100 Subject: [PATCH 08/15] Set IF debuggunverify click events --- ...tlin-compiler-11600878841597996578.salive} | 0 examples/minestom/build.gradle | 1 + .../runtime/SampleServer.kt | 18 +++++++-- .../runtime/command/GamemodeCommand.kt | 38 +++++++++++++++++++ .../runtime/view/Failing.kt | 2 +- .../runtime/view/SimplePagination.kt | 4 +- examples/paper/build.gradle | 1 + .../inventoryframework/IFInventoryListener.kt | 37 +++++++++--------- .../context/SlotClickContext.kt | 4 +- .../pipeline/CancelledCloseInterceptor.kt | 8 ++-- .../pipeline/GlobalClickInterceptor.kt | 9 ++--- .../pipeline/ItemClickInterceptor.kt | 7 ++-- 12 files changed, 90 insertions(+), 39 deletions(-) rename .kotlin/sessions/{kotlin-compiler-9388713202489506517.salive => kotlin-compiler-11600878841597996578.salive} (100%) create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/GamemodeCommand.kt diff --git a/.kotlin/sessions/kotlin-compiler-9388713202489506517.salive b/.kotlin/sessions/kotlin-compiler-11600878841597996578.salive similarity index 100% rename from .kotlin/sessions/kotlin-compiler-9388713202489506517.salive rename to .kotlin/sessions/kotlin-compiler-11600878841597996578.salive diff --git a/examples/minestom/build.gradle b/examples/minestom/build.gradle index 979f58de5..d57be9ec0 100644 --- a/examples/minestom/build.gradle +++ b/examples/minestom/build.gradle @@ -25,5 +25,6 @@ java { } application { + applicationDefaultJvmArgs = ["-Dme.devnatan.inventoryframework.debug=true"] mainClass = 'me.devnatan.inventoryframework.runtime.SampleServerKt' } \ No newline at end of file diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt index f8d89ccc0..015269090 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt @@ -1,23 +1,28 @@ package me.devnatan.inventoryframework.runtime import me.devnatan.inventoryframework.ViewFrame +import me.devnatan.inventoryframework.runtime.command.GamemodeCommand import me.devnatan.inventoryframework.runtime.command.IFExampleCommand import me.devnatan.inventoryframework.runtime.view.Failing import me.devnatan.inventoryframework.runtime.view.SimplePagination import net.minestom.server.MinecraftServer import net.minestom.server.coordinate.Pos import net.minestom.server.entity.PlayerSkin +import net.minestom.server.event.inventory.InventoryPreClickEvent import net.minestom.server.event.player.AsyncPlayerConfigurationEvent import net.minestom.server.event.player.PlayerSkinInitEvent +import net.minestom.server.event.player.PlayerSpawnEvent import net.minestom.server.instance.LightingChunk import net.minestom.server.instance.block.Block +import net.minestom.server.inventory.TransactionOption +import net.minestom.server.item.ItemStack +import net.minestom.server.item.Material class SampleServer { init { val server = MinecraftServer.init() val instanceManager = MinecraftServer.getInstanceManager() - MinecraftServer.getCommandManager().register() // Create word filled with quartz blocks up to height 50 val instance = instanceManager.createInstanceContainer() @@ -35,16 +40,23 @@ class SampleServer { handler.addListener(PlayerSkinInitEvent::class.java) { event -> event.skin = PlayerSkin.fromUsername(event.player.username) } + handler.addListener(PlayerSpawnEvent::class.java) { event -> + event.player.inventory.addItemStacks(ExampleUtil.getRandomItems(20), TransactionOption.ALL) + event.player.inventory.addItemStack(ItemStack.of(Material.OAK_PLANKS, 64), TransactionOption.ALL) + } val viewFrame = ViewFrame.create() .with(Failing(), SimplePagination()) .register() - MinecraftServer.getCommandManager().register(IFExampleCommand(viewFrame)) + MinecraftServer.getCommandManager().register( + IFExampleCommand(viewFrame), + GamemodeCommand(viewFrame) + ) + server.start("0.0.0.0", 25565) } - } fun main() { diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/GamemodeCommand.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/GamemodeCommand.kt new file mode 100644 index 000000000..679e37bac --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/GamemodeCommand.kt @@ -0,0 +1,38 @@ +package me.devnatan.inventoryframework.runtime.command + +import me.devnatan.inventoryframework.ViewFrame +import net.minestom.server.command.CommandSender +import net.minestom.server.command.builder.Command +import net.minestom.server.command.builder.CommandContext +import net.minestom.server.command.builder.arguments.Argument +import net.minestom.server.command.builder.arguments.ArgumentType +import net.minestom.server.command.builder.suggestion.SuggestionEntry +import net.minestom.server.entity.GameMode +import net.minestom.server.entity.Player +import java.util.* + +class GamemodeCommand(private val viewFrame: ViewFrame) : Command("gamemode") { + + private val gamemodeArg: Argument = ArgumentType.Enum("gameMode", GameMode::class.java) + .setSuggestionCallback { _, _, suggestion -> + for (gameMode in GameMode.entries) { + suggestion.addEntry(SuggestionEntry(gameMode.name.lowercase())) + } + } + + init { + setDefaultExecutor(::onCommand) + addSyntax(::onCommand, gamemodeArg) + } + + + private fun onCommand(sender: CommandSender, ctx: CommandContext) { + if (sender !is Player) { + sender.sendMessage("This command can only be executed by players.") + return + } + + val gameMode: GameMode = ctx.get(gamemodeArg) + sender.gameMode = gameMode + } +} \ No newline at end of file diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt index f5ed79966..26607c207 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/Failing.kt @@ -37,7 +37,7 @@ class Failing : View() { } render.layoutSlot('C', displayItem(Material.STONE, "Click me and I will fail")) - .onClick { ctx: SlotClickContext? -> + .onClick { _ -> throw IllegalStateException("This is a failing inventory") } } diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt index bdf628dfe..8185f88a1 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt @@ -41,10 +41,10 @@ class SimplePagination : View() { render.layoutSlot('P', previousItem) .displayIf(BooleanSupplier { state[render].canBack() }) .updateOnStateChange(state) - .onClick { ctx: SlotClickContext? -> state[ctx!!].back() } + .onClick { ctx: SlotClickContext -> state[ctx].back() } render.layoutSlot('N', nextItem) .displayIf(BooleanSupplier { state[render].canAdvance() }) .updateOnStateChange(state) - .onClick { ctx: SlotClickContext? -> state[ctx!!].advance() } + .onClick { ctx: SlotClickContext -> state[ctx].advance() } } } diff --git a/examples/paper/build.gradle b/examples/paper/build.gradle index 2098aeff1..0fc30682e 100644 --- a/examples/paper/build.gradle +++ b/examples/paper/build.gradle @@ -25,6 +25,7 @@ shadowJar { } runServer { + jvmArgs("-Dme.devnatan.inventoryframework.debug=true") minecraftVersion("1.21.3") } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt index 53f35a649..1a3345066 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt @@ -11,9 +11,10 @@ import net.minestom.server.event.EventFilter import net.minestom.server.event.EventNode import net.minestom.server.event.inventory.InventoryCloseEvent import net.minestom.server.event.inventory.InventoryPreClickEvent -import net.minestom.server.event.item.ItemDropEvent import net.minestom.server.event.item.PickupItemEvent import net.minestom.server.inventory.PlayerInventory +import net.minestom.server.inventory.click.ClickType +import kotlin.jvm.optionals.getOrNull internal class IFInventoryListener( private val viewFrame: ViewFrame @@ -25,16 +26,11 @@ internal class IFInventoryListener( .setPriority(10) .addListener(InventoryPreClickEvent::class.java, this::onInventoryClick) .addListener(InventoryCloseEvent::class.java, this::onInventoryClose) - val playerNode = EventNode.type("IF-player", EventFilter.PLAYER) - { _, p -> viewFrame.getViewer(p) != null } - .setPriority(10) - .addListener(ItemDropEvent::class.java, this::onItemDrop) val entityEvent = EventNode.type("IF-entity", EventFilter.ENTITY) { _, e -> e is Player && viewFrame.getViewer(e) != null } .setPriority(10) .addListener(PickupItemEvent::class.java, this::onItemPickup) handler.addChild(inventoryNode) - handler.addChild(playerNode) handler.addChild(entityEvent) } @@ -44,12 +40,26 @@ internal class IFInventoryListener( val player = event.player val viewer = viewFrame.getViewer(player) ?: return + if (event.clickType == ClickType.DROP) { + val context: IFContext = viewer.activeContext + if (!context.config.isOptionSet(ViewConfig.CANCEL_ON_DROP)) return + + event.isCancelled = context.config.getOptionValue(ViewConfig.CANCEL_ON_DROP) + return + } + if (event.clickType == ClickType.LEFT_DRAGGING || event.clickType == ClickType.RIGHT_DRAGGING) { + val context: IFContext = viewer.activeContext + if (!context.config.isOptionSet(ViewConfig.CANCEL_ON_DRAG)) return + + event.isCancelled = context.config.getOptionValue(ViewConfig.CANCEL_ON_DRAG) + return + } + val context: IFRenderContext = viewer.activeContext - val clickedComponent: me.devnatan.inventoryframework.component.Component = - context.getComponentsAt(event.slot).stream() + val clickedComponent = context.getComponentsAt(event.slot).stream() .filter { obj: me.devnatan.inventoryframework.component.Component -> obj.isVisible } .findFirst() - .orElse(null) + .getOrNull() val clickedContainer = if (event.inventory is PlayerInventory) viewer.selfContainer else @@ -81,13 +91,4 @@ internal class IFInventoryListener( event.isCancelled = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP) } - - fun onItemDrop(event: ItemDropEvent) { - val viewer = viewFrame.getViewer(event.getPlayer()) ?: return - - val context: IFContext = viewer.activeContext - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) return - - event.isCancelled = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP) - } } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt index 335ecf57d..e366648c7 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt @@ -67,7 +67,7 @@ class SlotClickContext constructor( } override fun isMiddleClick(): Boolean { - return clickOrigin.clickType == ClickType.CHANGE_HELD + return false } override fun isShiftClick(): Boolean { @@ -80,7 +80,7 @@ class SlotClickContext constructor( } override fun isOutsideClick(): Boolean { - return clickOrigin.inventory == null; + return clickOrigin.slot < 0; } override fun getClickIdentifier(): String { diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt index 91ee48d65..dd0ce026f 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/CancelledCloseInterceptor.kt @@ -5,11 +5,11 @@ import me.devnatan.inventoryframework.context.CloseContext class CancelledCloseInterceptor : PipelineInterceptor { - override fun intercept(pipeline: PipelineContext, context: VirtualView) { - if (context !is CloseContext) return + override fun intercept(pipeline: PipelineContext, subject: VirtualView) { + if (subject !is CloseContext) return - if (!context.isCancelled) return + if (!subject.isCancelled) return - context.root.nextTick { context.viewer.open(context.container) } + subject.root.nextTick { subject.viewer.open(subject.container) } } } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt index d65e52bcf..1de0d014c 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt @@ -13,12 +13,11 @@ class GlobalClickInterceptor : PipelineInterceptor { override fun intercept(pipeline: PipelineContext, subject: VirtualView) { if (subject !is SlotClickContext) return - val context = subject as SlotClickContext - val event: InventoryPreClickEvent = context.clickOrigin + val event: InventoryPreClickEvent = subject.clickOrigin // inherit cancellation so we can un-cancel it - context.isCancelled = - event.isCancelled || context.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true) - context.root.onClick(context) + subject.isCancelled = + event.isCancelled || subject.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true) + subject.root.onClick(subject) } } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt index e7033a35a..ac93429e7 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/ItemClickInterceptor.kt @@ -12,17 +12,16 @@ class ItemClickInterceptor : PipelineInterceptor { override fun intercept(pipeline: PipelineContext, subject: VirtualView) { if (subject !is SlotClickContext) return - val context = subject as SlotClickContext - val event: InventoryPreClickEvent = context.clickOrigin + val event: InventoryPreClickEvent = subject.clickOrigin event.inventory ?: return - val component = context.component ?: return + val component = subject.component ?: return if (component is ItemComponent) { val item: ItemComponent = component // inherit cancellation so we can un-cancel it - context.isCancelled = item.isCancelOnClick + subject.isCancelled = item.isCancelOnClick } } } From 8822e226cbeaba8386072caba0d1baf8042cc03d Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 23:15:43 +0100 Subject: [PATCH 09/15] Add Scheduled test view --- ...kotlin-compiler-914906829911856868.salive} | 0 .../runtime/SampleServer.kt | 8 +++- .../runtime/command/IFExampleCommand.kt | 4 +- .../runtime/view/ScheduledView.kt | 38 +++++++++++++++++++ .../runtime/view/SimplePagination.kt | 4 +- 5 files changed, 49 insertions(+), 5 deletions(-) rename .kotlin/sessions/{kotlin-compiler-11600878841597996578.salive => kotlin-compiler-914906829911856868.salive} (100%) create mode 100644 examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/ScheduledView.kt diff --git a/.kotlin/sessions/kotlin-compiler-11600878841597996578.salive b/.kotlin/sessions/kotlin-compiler-914906829911856868.salive similarity index 100% rename from .kotlin/sessions/kotlin-compiler-11600878841597996578.salive rename to .kotlin/sessions/kotlin-compiler-914906829911856868.salive diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt index 015269090..a8858095b 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt @@ -4,6 +4,7 @@ import me.devnatan.inventoryframework.ViewFrame import me.devnatan.inventoryframework.runtime.command.GamemodeCommand import me.devnatan.inventoryframework.runtime.command.IFExampleCommand import me.devnatan.inventoryframework.runtime.view.Failing +import me.devnatan.inventoryframework.runtime.view.ScheduledView import me.devnatan.inventoryframework.runtime.view.SimplePagination import net.minestom.server.MinecraftServer import net.minestom.server.coordinate.Pos @@ -46,12 +47,15 @@ class SampleServer { } val viewFrame = ViewFrame.create() - .with(Failing(), SimplePagination()) + .with( + Failing(), + SimplePagination(), + ScheduledView()) .register() MinecraftServer.getCommandManager().register( IFExampleCommand(viewFrame), - GamemodeCommand(viewFrame) + GamemodeCommand(viewFrame), ) diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt index b06a28e0b..bd4910155 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/command/IFExampleCommand.kt @@ -2,6 +2,7 @@ package me.devnatan.inventoryframework.runtime.command import me.devnatan.inventoryframework.ViewFrame import me.devnatan.inventoryframework.runtime.view.Failing +import me.devnatan.inventoryframework.runtime.view.ScheduledView import me.devnatan.inventoryframework.runtime.view.SimplePagination import net.minestom.server.command.CommandSender import net.minestom.server.command.builder.Command @@ -16,7 +17,8 @@ class IFExampleCommand(private val viewFrame: ViewFrame) : Command("ifexample") private val availableViews = mapOf( "failing" to Failing::class.java, - "simple-pagination" to SimplePagination::class.java + "simple-pagination" to SimplePagination::class.java, + "scheduled" to ScheduledView::class.java ); private val arg: Argument = diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/ScheduledView.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/ScheduledView.kt new file mode 100644 index 000000000..b248969c9 --- /dev/null +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/ScheduledView.kt @@ -0,0 +1,38 @@ +package me.devnatan.inventoryframework.runtime.view + +import me.devnatan.inventoryframework.View +import me.devnatan.inventoryframework.ViewConfigBuilder +import me.devnatan.inventoryframework.context.Context +import me.devnatan.inventoryframework.context.RenderContext +import me.devnatan.inventoryframework.context.SlotClickContext +import me.devnatan.inventoryframework.runtime.ExampleUtil +import net.minestom.server.item.Material +import java.time.Duration + +class ScheduledView : View() { + + val counter = mutableState(0) + + override fun onInit(config: ViewConfigBuilder) { + config.cancelOnClick() + config.size(3) + config.title("Simple Pagination") + config.layout( + " ", + " C ", + "B ") + config.scheduleUpdate(20) + } + + override fun onFirstRender(render: RenderContext) { + render.layoutSlot('C') + .onRender { + it.item = ExampleUtil.displayItem(Material.STONE, counter.increment(it).toString()) + } + + render.layoutSlot('B', ExampleUtil.displayItem(Material.PAPER, "Back")) + .displayIf(Context::canBack) + .onClick(SlotClickContext::back) + } + +} diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt index 8185f88a1..658b88da2 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/view/SimplePagination.kt @@ -39,11 +39,11 @@ class SimplePagination : View() { val previousItem = displayItem(Material.ARROW, "Previous") val nextItem = displayItem(Material.ARROW, "Next") render.layoutSlot('P', previousItem) - .displayIf(BooleanSupplier { state[render].canBack() }) + .displayIf({ctx -> state[ctx].canBack() }) .updateOnStateChange(state) .onClick { ctx: SlotClickContext -> state[ctx].back() } render.layoutSlot('N', nextItem) - .displayIf(BooleanSupplier { state[render].canAdvance() }) + .displayIf({ctx -> state[ctx].canAdvance()}) .updateOnStateChange(state) .onClick { ctx: SlotClickContext -> state[ctx].advance() } } From ed03f46704e6851e14461aa03919e0aad63a9aef Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Tue, 4 Feb 2025 23:29:15 +0100 Subject: [PATCH 10/15] remove some unwanted tabs --- .kotlin/sessions/kotlin-compiler-914906829911856868.salive | 0 .../me/devnatan/inventoryframework/runtime/ExampleUtil.kt | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 .kotlin/sessions/kotlin-compiler-914906829911856868.salive diff --git a/.kotlin/sessions/kotlin-compiler-914906829911856868.salive b/.kotlin/sessions/kotlin-compiler-914906829911856868.salive deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt index da612f191..0638cf19e 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/ExampleUtil.kt @@ -8,7 +8,7 @@ import java.util.* object ExampleUtil { @JvmStatic - fun getRandomItems(amount: Int): List { + fun getRandomItems(amount: Int): List { val materials = Material.values().toTypedArray() val random = Random() @@ -22,7 +22,7 @@ object ExampleUtil { } @JvmStatic - fun displayItem(material: Material, displayName: String): ItemStack { + fun displayItem(material: Material, displayName: String): ItemStack { return ItemStack.of(material).withCustomName(Component.text(displayName)) } } From b562d67ee613ddd5cb37013b7ee87b87a7e62ec8 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Wed, 5 Feb 2025 01:41:07 +0100 Subject: [PATCH 11/15] Give a paren event node to be able to do pre filtering, allows to make per instance ViewFrame --- ...kotlin-compiler-8176556217718788239.salive | 0 .../runtime/SampleServer.kt | 6 ++-- .../inventoryframework/IFInventoryListener.kt | 17 +++++------ .../devnatan/inventoryframework/ViewFrame.kt | 30 ++++++++++--------- 4 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 .kotlin/sessions/kotlin-compiler-8176556217718788239.salive diff --git a/.kotlin/sessions/kotlin-compiler-8176556217718788239.salive b/.kotlin/sessions/kotlin-compiler-8176556217718788239.salive new file mode 100644 index 000000000..e69de29bb diff --git a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt index a8858095b..5a7b25898 100644 --- a/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt +++ b/examples/minestom/src/main/kotlin/me/devnatan/inventoryframework/runtime/SampleServer.kt @@ -9,7 +9,9 @@ import me.devnatan.inventoryframework.runtime.view.SimplePagination import net.minestom.server.MinecraftServer import net.minestom.server.coordinate.Pos import net.minestom.server.entity.PlayerSkin -import net.minestom.server.event.inventory.InventoryPreClickEvent +import net.minestom.server.event.Event +import net.minestom.server.event.EventFilter +import net.minestom.server.event.EventNode import net.minestom.server.event.player.AsyncPlayerConfigurationEvent import net.minestom.server.event.player.PlayerSkinInitEvent import net.minestom.server.event.player.PlayerSpawnEvent @@ -46,7 +48,7 @@ class SampleServer { event.player.inventory.addItemStack(ItemStack.of(Material.OAK_PLANKS, 64), TransactionOption.ALL) } - val viewFrame = ViewFrame.create() + val viewFrame = ViewFrame.create(handler) .with( Failing(), SimplePagination(), diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt index 1a3345066..dc135b0e3 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/IFInventoryListener.kt @@ -5,33 +5,30 @@ import me.devnatan.inventoryframework.context.IFContext import me.devnatan.inventoryframework.context.IFRenderContext import me.devnatan.inventoryframework.context.IFSlotClickContext import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases -import net.minestom.server.MinecraftServer import net.minestom.server.entity.Player import net.minestom.server.event.EventFilter import net.minestom.server.event.EventNode import net.minestom.server.event.inventory.InventoryCloseEvent import net.minestom.server.event.inventory.InventoryPreClickEvent import net.minestom.server.event.item.PickupItemEvent +import net.minestom.server.event.trait.EntityEvent import net.minestom.server.inventory.PlayerInventory import net.minestom.server.inventory.click.ClickType import kotlin.jvm.optionals.getOrNull internal class IFInventoryListener( - private val viewFrame: ViewFrame + private val viewFrame: ViewFrame, + handler: EventNode ) { init { - val handler = MinecraftServer.getGlobalEventHandler() - val inventoryNode = EventNode.type("IF-inventory", EventFilter.INVENTORY) - .setPriority(10) - .addListener(InventoryPreClickEvent::class.java, this::onInventoryClick) - .addListener(InventoryCloseEvent::class.java, this::onInventoryClose) - val entityEvent = EventNode.type("IF-entity", EventFilter.ENTITY) + val node = EventNode.type("IF", EventFilter.ENTITY) { _, e -> e is Player && viewFrame.getViewer(e) != null } .setPriority(10) .addListener(PickupItemEvent::class.java, this::onItemPickup) - handler.addChild(inventoryNode) - handler.addChild(entityEvent) + .addListener(InventoryPreClickEvent::class.java, this::onInventoryClick) + .addListener(InventoryCloseEvent::class.java, this::onInventoryClose) + handler.addChild(node) } fun onInventoryClick(event: InventoryPreClickEvent) { diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt index d66f280db..13848e3ca 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/ViewFrame.kt @@ -7,11 +7,13 @@ import me.devnatan.inventoryframework.feature.FeatureInstaller import me.devnatan.inventoryframework.internal.MinestomElementFactory import me.devnatan.inventoryframework.internal.PlatformUtils import net.minestom.server.entity.Player +import net.minestom.server.event.EventNode +import net.minestom.server.event.trait.EntityEvent import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.ApiStatus.Experimental import java.util.function.UnaryOperator -class ViewFrame private constructor() : IFViewFrame() { +class ViewFrame private constructor(private val parentNode: EventNode) : IFViewFrame() { private val featureInstaller: FeatureInstaller = DefaultFeatureInstaller( this @@ -57,7 +59,7 @@ class ViewFrame private constructor() : IFViewFrame() { * @return The id of the newly created [IFContext]. */ @Experimental - fun open(viewClass: Class, players: Collection): String { + fun open(viewClass: Class, players: Collection): String { return open(viewClass, players, null) } @@ -78,8 +80,8 @@ class ViewFrame private constructor() : IFViewFrame() { */ @Experimental fun open( - viewClass: Class, - players: Collection, + viewClass: Class, + players: Collection, initialData: Any? ): String { return internalOpen(viewClass, players, initialData) @@ -97,7 +99,7 @@ class ViewFrame private constructor() : IFViewFrame() { */ @Experimental fun openActive( - viewClass: Class, contextId: String, player: Player + viewClass: Class, contextId: String, player: Player ) { openActive(viewClass, contextId, player, null) } @@ -115,7 +117,7 @@ class ViewFrame private constructor() : IFViewFrame() { */ @Experimental fun openActive( - viewClass: Class, + viewClass: Class, contextId: String, player: Player, initialData: Any? @@ -154,8 +156,8 @@ class ViewFrame private constructor() : IFViewFrame() { endlessContextInfo: EndlessContextInfo, player: Player, initialData: Any? ) { openActive( - endlessContextInfo.getView().javaClass as Class, - endlessContextInfo.getContextId(), + endlessContextInfo.view.javaClass as Class, + endlessContextInfo.contextId, player, initialData ) @@ -169,7 +171,7 @@ class ViewFrame private constructor() : IFViewFrame() { PlatformUtils.setFactory(MinestomElementFactory()) pipeline.execute(FRAME_REGISTERED, this) initializeViews() - IFInventoryListener(this) + IFInventoryListener(this, parentNode) return this } @@ -229,9 +231,9 @@ class ViewFrame private constructor() : IFViewFrame() { * @return An instance of the installed feature. */ fun install( - feature: Feature, configure: UnaryOperator + feature: Feature, configure: UnaryOperator ): ViewFrame { - featureInstaller.install(feature, configure) + featureInstaller.install(feature, configure) IFDebug.debug("Feature %s installed", feature.name()) return this } @@ -242,7 +244,7 @@ class ViewFrame private constructor() : IFViewFrame() { * @param feature The feature to be installed. * @return This view frame. */ - fun install(feature: Feature<*, *, ViewFrame?>): ViewFrame { + fun install(feature: Feature<*, *, ViewFrame>): ViewFrame { install(feature, UnaryOperator.identity()) return this } @@ -284,8 +286,8 @@ class ViewFrame private constructor() : IFViewFrame() { * @param owner The plugin that owns this view frame. * @return A new ViewFrame instance. */ - fun create(): ViewFrame { - return ViewFrame() + fun create(parentNode: EventNode): ViewFrame { + return ViewFrame(parentNode) } private val LOGGER = java.util.logging.Logger.getLogger("IF") From 27294ae684419154e67f548934bc938695fb97f7 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Wed, 5 Feb 2025 01:48:39 +0100 Subject: [PATCH 12/15] Suppress warnings for unchecked casts in Comonent Builder --- .../component/MinestomIemComponentBuilder.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt index b91eab3de..7171737c5 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomIemComponentBuilder.kt @@ -19,7 +19,7 @@ import java.util.function.Predicate import java.util.function.Supplier class MinestomIemComponentBuilder private constructor( - root: VirtualView, + private val root: VirtualView, slot: Int, item: ItemStack?, renderHandler: Consumer?, @@ -44,7 +44,6 @@ class MinestomIemComponentBuilder private constructor( displayCondition ), ItemComponentBuilder, ComponentFactory { - private val root: VirtualView = root private var slot: Int private var item: ItemStack? private var renderHandler: Consumer? @@ -124,6 +123,7 @@ class MinestomIemComponentBuilder private constructor( * @param renderHandler The render handler. * @return This item builder. */ + @Suppress("UNCHECKED_CAST") fun onRender(renderHandler: Consumer?): MinestomIemComponentBuilder { this.renderHandler = renderHandler as? Consumer return this @@ -154,6 +154,7 @@ class MinestomIemComponentBuilder private constructor( * @param clickHandler The click handler. * @return This item builder. */ + @Suppress("UNCHECKED_CAST") fun onClick(clickHandler: Consumer?): MinestomIemComponentBuilder { this.clickHandler = clickHandler as? Consumer return this @@ -179,6 +180,7 @@ class MinestomIemComponentBuilder private constructor( * @param updateHandler The update handler. * @return This item builder. */ + @Suppress("UNCHECKED_CAST") fun onUpdate(updateHandler: Consumer?): MinestomIemComponentBuilder { this.updateHandler = updateHandler as? Consumer return this From bb761ef9b253d2f8b7885813234a38a3a1b22d57 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Wed, 5 Feb 2025 23:39:42 +0100 Subject: [PATCH 13/15] Remvoe unsed logger --- ...kotlin-compiler-8176556217718788239.salive | 0 .../logging/BukkitLogger.kt | 29 ------------------- 2 files changed, 29 deletions(-) delete mode 100644 .kotlin/sessions/kotlin-compiler-8176556217718788239.salive delete mode 100644 inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt diff --git a/.kotlin/sessions/kotlin-compiler-8176556217718788239.salive b/.kotlin/sessions/kotlin-compiler-8176556217718788239.salive deleted file mode 100644 index e69de29bb..000000000 diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt deleted file mode 100644 index fa0d545e5..000000000 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/logging/BukkitLogger.kt +++ /dev/null @@ -1,29 +0,0 @@ -package me.devnatan.inventoryframework.logging - -class BukkitLogger( - private val logger: java.util.logging.Logger, - private val viewName: String, - private val isShaded: Boolean -) : Logger { - private var finalPrefix: String? = null - - override fun getPrefix(): String? { - if (finalPrefix == null) { - finalPrefix = String.format("%s[%s]", if (isShaded) "[IF]" else "", viewName) + " " - } - - return finalPrefix - } - - override fun debug(message: String) { - logger.info(prefix + message) - } - - override fun warn(message: String) { - logger.warning(prefix + message) - } - - override fun error(message: String) { - logger.severe(prefix + message) - } -} From 7ee0744f758320556eae21d8377057bf7bbc1441 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Wed, 5 Feb 2025 23:41:51 +0100 Subject: [PATCH 14/15] Add missing API internals --- .../me/devnatan/inventoryframework/context/CloseContext.kt | 3 ++- .../me/devnatan/inventoryframework/context/OpenContext.kt | 3 ++- .../me/devnatan/inventoryframework/context/SlotClickContext.kt | 3 ++- .../me/devnatan/inventoryframework/context/SlotContext.kt | 3 ++- .../devnatan/inventoryframework/context/SlotRenderContext.kt | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt index 02c77b61d..e199be339 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/CloseContext.kt @@ -10,10 +10,11 @@ import me.devnatan.inventoryframework.state.StateValue import me.devnatan.inventoryframework.state.StateWatcher import net.kyori.adventure.text.Component import net.minestom.server.entity.Player +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.UnmodifiableView import java.util.* -class CloseContext(subject: Viewer, private val parent: IFRenderContext) : +class CloseContext @ApiStatus.Internal constructor(subject: Viewer, private val parent: IFRenderContext) : PlatformConfinedContext(), IFCloseContext, Context { private val subject: Viewer = subject diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt index 5f558576a..1881f96cc 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/OpenContext.kt @@ -3,6 +3,7 @@ package me.devnatan.inventoryframework.context import me.devnatan.inventoryframework.* import net.kyori.adventure.text.Component import net.minestom.server.entity.Player +import org.jetbrains.annotations.ApiStatus import java.util.* import java.util.concurrent.CompletableFuture @@ -20,7 +21,7 @@ import java.util.concurrent.CompletableFuture * Must be provided even in non-shared context cases. * @param initialData Initial data provided by the user. */ -class OpenContext constructor( +class OpenContext @ApiStatus.Internal constructor( private val root: View, private val subject: Viewer?, private val viewers: Map, diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt index e366648c7..ede4d669b 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotClickContext.kt @@ -9,8 +9,9 @@ import net.minestom.server.event.inventory.InventoryPreClickEvent import net.minestom.server.inventory.PlayerInventory import net.minestom.server.inventory.click.ClickType import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.ApiStatus -class SlotClickContext constructor( +class SlotClickContext @ApiStatus.Internal constructor( slot: Int, parent: IFRenderContext, private val whoClicked: Viewer, diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt index 97cb41ce0..8e3633b92 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotContext.kt @@ -10,10 +10,11 @@ import me.devnatan.inventoryframework.state.StateValue import me.devnatan.inventoryframework.state.StateWatcher import net.minestom.server.entity.Player import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.UnmodifiableView import java.util.* -abstract class SlotContext protected constructor( +abstract class SlotContext @ApiStatus.Internal protected constructor( private var slot: Int, private val parent: IFRenderContext ) : PlatformContext(), diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt index c690d6e7a..d79060df1 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt @@ -5,9 +5,10 @@ import me.devnatan.inventoryframework.RootView import me.devnatan.inventoryframework.Viewer import net.minestom.server.entity.Player import net.minestom.server.item.ItemStack +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.UnknownNullability -class SlotRenderContext constructor(slot: Int, parent: IFRenderContext, private val viewer: Viewer?) : +class SlotRenderContext @ApiStatus.Internal constructor(slot: Int, parent: IFRenderContext, private val viewer: Viewer?) : SlotContext(slot, parent), IFSlotRenderContext { override val player: Player = (viewer as MinestomViewer).player From 3dc0fbf28f9031b07de5c7ef1622dac533295907 Mon Sep 17 00:00:00 2001 From: Nico Lube Date: Wed, 5 Feb 2025 23:44:59 +0100 Subject: [PATCH 15/15] Add .kotlin to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3cec2558e..80982cfbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .gradle/ .idea/ +.kotlin/ build/ libs/ \ No newline at end of file