diff options
| author | Linnea Gräf <nea@nea.moe> | 2025-11-24 23:42:00 +0100 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2025-11-24 23:42:00 +0100 |
| commit | 6dd14e7225ff60361fe9f87020c424c0eb89a68b (patch) | |
| tree | 6977d5a71850924becc71b1ac53f1865a1c8330a /src/main | |
| parent | 3f33928c8fefe4816af9d538fa3fce48d5e76f7c (diff) | |
| download | Firmament-6dd14e7225ff60361fe9f87020c424c0eb89a68b.tar.gz Firmament-6dd14e7225ff60361fe9f87020c424c0eb89a68b.tar.bz2 Firmament-6dd14e7225ff60361fe9f87020c424c0eb89a68b.zip | |
feat: firmament api
Diffstat (limited to 'src/main')
13 files changed, 182 insertions, 42 deletions
diff --git a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java index 881990c..98be517 100644 --- a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java +++ b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java @@ -2,11 +2,13 @@ package moe.nea.firmament.init; import me.shedaniel.mm.api.ClassTinkerers; +import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.input.CharacterEvent; import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; @@ -27,6 +29,8 @@ public class HandledScreenRiser extends RiserUtils { Intermediary.InterClass KeyInput = Intermediary.<KeyEvent>intermediaryClass(); Intermediary.InterClass CharInput = Intermediary.<CharacterEvent>intermediaryClass(); Intermediary.InterClass HandledScreen = Intermediary.<AbstractContainerScreen>intermediaryClass(); + Intermediary.InterClass AbstractContainerEventHandler = Intermediary.<AbstractContainerEventHandler>intermediaryClass(); + Intermediary.InterClass MouseButtonEvent = Intermediary.<MouseButtonEvent>intermediaryClass(); Intermediary.InterMethod mouseScrolled = Intermediary.intermediaryMethod( GuiEventListener::mouseScrolled, Intermediary.ofClass(boolean.class), @@ -35,6 +39,14 @@ public class HandledScreenRiser extends RiserUtils { Intermediary.ofClass(double.class), Intermediary.ofClass(double.class) ); + Intermediary.InterMethod mouseClickedScreen = Intermediary.intermediaryMethod( + //onMouseClicked$firmament + GuiEventListener::mouseClicked, + Intermediary.ofClass(boolean.class), + MouseButtonEvent, + Intermediary.ofClass(boolean.class) + ); + ; Intermediary.InterMethod keyReleased = Intermediary.intermediaryMethod( GuiEventListener::keyReleased, Intermediary.ofClass(boolean.class), @@ -47,12 +59,12 @@ public class HandledScreenRiser extends RiserUtils { ); - @Override public void addTinkerers() { addTransformation(HandledScreen, this::addMouseScroll, true); addTransformation(HandledScreen, this::addKeyReleased, true); addTransformation(HandledScreen, this::addCharTyped, true); + addTransformation(Screen, this::addMouseClicked, true); } /** @@ -86,7 +98,7 @@ public class HandledScreenRiser extends RiserUtils { void addKeyReleased(ClassNode classNode) { addSuperInjector( - classNode, keyReleased.mapped(), keyReleased.mappedDesc(), "keyReleased_firmament", + classNode, keyReleased.mapped(), keyReleased.mappedDesc(), HandledScreen, Screen, "keyReleased_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); @@ -95,9 +107,25 @@ public class HandledScreenRiser extends RiserUtils { }); } + void addMouseClicked(ClassNode classNode) { + addSuperInjector( + classNode, mouseClickedScreen.mapped(), mouseClickedScreen.mappedDesc(), + Screen, AbstractContainerEventHandler, "onMouseClicked$firmament", + insns -> { + // load this + insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + // load mouse event + insns.add(new VarInsnNode(Opcodes.ALOAD, 1)); + // load doubled + insns.add(new VarInsnNode(Opcodes.ILOAD, 2)); + } + ); + } + void addCharTyped(ClassNode classNode) { addSuperInjector( - classNode, charTyped.mapped(), charTyped.mappedDesc(), "charTyped_firmament", + classNode, charTyped.mapped(), charTyped.mappedDesc(), + HandledScreen, Screen, "charTyped_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); @@ -110,6 +138,8 @@ public class HandledScreenRiser extends RiserUtils { ClassNode classNode, String name, Type desc, + Intermediary.InterClass currentClass, + Intermediary.InterClass parentClass, String firmamentName, Consumer<InsnList> loadArgs ) { @@ -125,7 +155,7 @@ public class HandledScreenRiser extends RiserUtils { var insns = keyReleasedNode.instructions; loadArgs.accept(insns); // INVOKESPECIAL call super method - insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Screen.mapped().getInternalName(), + insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, parentClass.mapped().getInternalName(), name, desc.getDescriptor())); // IRETURN return int on stack (booleans are int at runtime) insns.add(new InsnNode(Opcodes.IRETURN)); @@ -134,7 +164,7 @@ public class HandledScreenRiser extends RiserUtils { insertTrueHandler(keyReleasedNode, loadArgs, insns -> { // INVOKEVIRTUAL call custom handler insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, - HandledScreen.mapped().getInternalName(), + currentClass.mapped().getInternalName(), firmamentName, desc.getDescriptor())); }); @@ -143,7 +173,7 @@ public class HandledScreenRiser extends RiserUtils { void addMouseScroll(ClassNode classNode) { addSuperInjector( - classNode, mouseScrolled.mapped(), mouseScrolled.mappedDesc(), "mouseScrolled_firmament", + classNode, mouseScrolled.mapped(), mouseScrolled.mappedDesc(), HandledScreen, Screen, "mouseScrolled_firmament", insns -> { // ALOAD 0, load this insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index bd2b594..c7555fb 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -49,24 +49,6 @@ public abstract class MixinHandledScreen<T extends AbstractContainerMenu> { this.playerInventory = inventory; } - @Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;keyPressed(Lnet/minecraft/client/input/KeyEvent;)Z", shift = At.Shift.BEFORE), cancellable = true) - public void onKeyPressed(KeyEvent input, CallbackInfoReturnable<Boolean> cir) { - if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent( - (AbstractContainerScreen<?>) (Object) this, - GenericInputAction.of(input), - InputModifiers.of(input))).getCancelled()) { - cir.setReturnValue(true); - } - } - - @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) - public void onMouseClicked(MouseButtonEvent click, boolean doubled, CallbackInfoReturnable<Boolean> cir) { - if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent((AbstractContainerScreen<?>) (Object) this, - GenericInputAction.mouse(click), InputModifiers.current())).getCancelled()) { - cir.setReturnValue(true); - } - } - @Inject(method = "mouseReleased", at = @At("HEAD"), cancellable = true) private void onMouseReleased(MouseButtonEvent click, CallbackInfoReturnable<Boolean> cir) { var self = (AbstractContainerScreen<?>) (Object) this; diff --git a/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java b/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java new file mode 100644 index 0000000..edc8fd6 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/ScreenInputEvents.java @@ -0,0 +1,34 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.events.HandledScreenKeyPressedEvent; +import moe.nea.firmament.keybindings.GenericInputAction; +import moe.nea.firmament.keybindings.InputModifiers; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Screen.class) +public class ScreenInputEvents { + @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) + public void onKeyPressed(KeyEvent input, CallbackInfoReturnable<Boolean> cir) { + if (HandledScreenKeyPressedEvent.Companion.publish(new HandledScreenKeyPressedEvent( + (Screen) (Object) this, + GenericInputAction.of(input), + InputModifiers.of(input))).getCancelled()) { + cir.setReturnValue(true); + } + } + + public boolean onMouseClicked$firmament(MouseButtonEvent click, boolean doubled) { + return HandledScreenKeyPressedEvent.Companion.publish( + new HandledScreenKeyPressedEvent((Screen) (Object) this, + GenericInputAction.mouse(click), InputModifiers.current())).getCancelled(); + } + + +} diff --git a/src/main/kotlin/Firmament.kt b/src/main/kotlin/Firmament.kt index 72f386d..68789f1 100644 --- a/src/main/kotlin/Firmament.kt +++ b/src/main/kotlin/Firmament.kt @@ -39,6 +39,7 @@ import moe.nea.firmament.events.TickEvent import moe.nea.firmament.events.registration.registerFirmamentEvents import moe.nea.firmament.features.FeatureManager import moe.nea.firmament.gui.config.storage.FirmamentConfigLoader +import moe.nea.firmament.impl.v1.FirmamentAPIImpl import moe.nea.firmament.repo.HypixelStaticData import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.MC @@ -123,6 +124,7 @@ object Firmament { globalJob.cancel() }) registerFirmamentEvents() + FirmamentAPIImpl.loadExtensions() ItemTooltipCallback.EVENT.register { stack, context, type, lines -> ItemTooltipEvent.publish(ItemTooltipEvent(stack, context, type, lines)) } diff --git a/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt index b9e23a2..5a5f1e9 100644 --- a/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt +++ b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt @@ -1,19 +1,20 @@ package moe.nea.firmament.events import org.lwjgl.glfw.GLFW +import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import moe.nea.firmament.keybindings.GenericInputAction import moe.nea.firmament.keybindings.InputModifiers import moe.nea.firmament.keybindings.SavedKeyBinding sealed interface HandledScreenInputEvent { - val screen: AbstractContainerScreen<*> + val screen: Screen val input: GenericInputAction val modifiers: InputModifiers } data class HandledScreenKeyPressedEvent( - override val screen: AbstractContainerScreen<*>, + override val screen: Screen, override val input: GenericInputAction, override val modifiers: InputModifiers, // TODO: val isRepeat: Boolean, diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt index 1a14f73..145ea35 100644 --- a/src/main/kotlin/features/debug/PowerUserTools.kt +++ b/src/main/kotlin/features/debug/PowerUserTools.kt @@ -13,6 +13,7 @@ import net.minecraft.world.item.Items import net.minecraft.nbt.ListTag import net.minecraft.nbt.NbtOps import net.minecraft.advancements.critereon.NbtPredicate +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization import net.minecraft.resources.ResourceLocation @@ -202,7 +203,7 @@ object PowerUserTools { .orThrow ClipboardUtils.setTextContent(nbt.toPrettyString()) lastCopiedStack = Pair(item, Component.translatableEscape("firmament.tooltip.copied.stack")) - } else if (it.matches(TConfig.copyTitle)) { + } else if (it.matches(TConfig.copyTitle) && it.screen is AbstractContainerScreen<*>) { val allTitles = ListTag() val inventoryNames = it.screen.menu.slots diff --git a/src/main/kotlin/features/inventory/WardrobeKeybinds.kt b/src/main/kotlin/features/inventory/WardrobeKeybinds.kt index b3d4bfd..8d4760b 100644 --- a/src/main/kotlin/features/inventory/WardrobeKeybinds.kt +++ b/src/main/kotlin/features/inventory/WardrobeKeybinds.kt @@ -1,6 +1,7 @@ package moe.nea.firmament.features.inventory import org.lwjgl.glfw.GLFW +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.world.item.Items import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.HandledScreenKeyPressedEvent @@ -29,6 +30,7 @@ object WardrobeKeybinds { @Subscribe fun switchSlot(event: HandledScreenKeyPressedEvent) { if (MC.player == null || MC.world == null || MC.interactionManager == null) return + if (event.screen !is AbstractContainerScreen<*>) return val regex = Regex("Wardrobe \\([12]/2\\)") if (!regex.matches(event.screen.title.string)) return diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButtons.kt b/src/main/kotlin/features/inventory/buttons/InventoryButtons.kt index fa376bc..eaa6138 100644 --- a/src/main/kotlin/features/inventory/buttons/InventoryButtons.kt +++ b/src/main/kotlin/features/inventory/buttons/InventoryButtons.kt @@ -11,6 +11,7 @@ import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.HandledScreenClickEvent import moe.nea.firmament.events.HandledScreenForegroundEvent import moe.nea.firmament.events.HandledScreenPushREIEvent +import moe.nea.firmament.impl.v1.FirmamentAPIImpl import moe.nea.firmament.util.MC import moe.nea.firmament.util.ScreenUtil import moe.nea.firmament.util.TimeMark @@ -40,9 +41,11 @@ object InventoryButtons { ) fun getValidButtons(screen: AbstractContainerScreen<*>): Sequence<InventoryButton> { - return DConfig.data.buttons.asSequence().filter { button -> - button.isValid() && (!TConfig.onlyInv || screen is InventoryScreen) + if (TConfig.onlyInv && screen !is InventoryScreen) return emptySequence() + if (FirmamentAPIImpl.extensions.any { it.shouldHideInventoryButtons(screen) }) { + return emptySequence() } + return DConfig.data.buttons.asSequence().filter(InventoryButton::isValid) } diff --git a/src/main/kotlin/features/items/recipes/ItemSlotWidget.kt b/src/main/kotlin/features/items/recipes/ItemSlotWidget.kt index b9832b7..c47c8ca 100644 --- a/src/main/kotlin/features/items/recipes/ItemSlotWidget.kt +++ b/src/main/kotlin/features/items/recipes/ItemSlotWidget.kt @@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.TooltipFlag +import moe.nea.firmament.api.v1.FirmamentItemWidget import moe.nea.firmament.events.ItemTooltipEvent import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.repo.ExpensiveItemCacheApi @@ -27,7 +28,8 @@ class ItemSlotWidget( var content: List<SBItemStack>, val slotKind: RecipeLayouter.SlotKind ) : RecipeWidget(), - RecipeLayouter.CyclingItemSlot { + RecipeLayouter.CyclingItemSlot, + FirmamentItemWidget { override var position = point override val size get() = Dimension(16, 16) val itemRect get() = Rectangle(position, Dimension(16, 16)) @@ -122,4 +124,17 @@ class ItemSlotWidget( // SAFE: content was just assigned to a non-empty list index = index.coerceIn(content.indices) } + + override fun getPlacement(): FirmamentItemWidget.Placement { + return FirmamentItemWidget.Placement.RECIPE_SCREEN + } + + @OptIn(ExpensiveItemCacheApi::class) + override fun getItemStack(): ItemStack { + return current().asImmutableItemStack() + } + + override fun getSkyBlockId(): String { + return current().skyblockId.neuItem + } } diff --git a/src/main/kotlin/features/items/recipes/RecipeRegistry.kt b/src/main/kotlin/features/items/recipes/RecipeRegistry.kt index cbe1558..c2df46f 100644 --- a/src/main/kotlin/features/items/recipes/RecipeRegistry.kt +++ b/src/main/kotlin/features/items/recipes/RecipeRegistry.kt @@ -3,7 +3,6 @@ package moe.nea.firmament.features.items.recipes import com.mojang.blaze3d.platform.InputConstants import io.github.moulberry.repo.IReloadable import io.github.moulberry.repo.NEURepository -import net.fabricmc.fabric.mixin.client.gametest.input.InputUtilMixin import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.ReloadRegistrationEvent @@ -29,13 +28,19 @@ object RecipeRegistry { @Subscribe - fun onDebugRecipe(event: HandledScreenKeyPressedEvent) { - if (event.matches(SavedKeyBinding.keyWithoutMods(InputConstants.KEY_R))) { - val stack = event.screen.focusedItemStack ?: return - val recipes = getRecipesFor(SBItemStack(stack)) - if (recipes.isEmpty()) return - MC.screen = RecipeScreen(recipes.toList()) - } + fun showUsages(event: HandledScreenKeyPressedEvent) { + val provider = + if (event.matches(SavedKeyBinding.keyWithoutMods(InputConstants.KEY_R))) { + ::getRecipesFor + } else if (event.matches(SavedKeyBinding.keyWithoutMods(InputConstants.KEY_U))) { + ::getUsagesFor + } else { + return + } + val stack = event.screen.focusedItemStack ?: return + val recipes = provider(SBItemStack(stack)) + if (recipes.isEmpty()) return + MC.screen = RecipeScreen(recipes.toList()) } diff --git a/src/main/kotlin/features/items/recipes/RecipeWidget.kt b/src/main/kotlin/features/items/recipes/RecipeWidget.kt index 5884129..b0591b2 100644 --- a/src/main/kotlin/features/items/recipes/RecipeWidget.kt +++ b/src/main/kotlin/features/items/recipes/RecipeWidget.kt @@ -34,4 +34,8 @@ abstract class RecipeWidget : GuiEventListener, Renderable, NarratableEntry { override fun isFocused(): Boolean { return this._focused } + + override fun isMouseOver(mouseX: Double, mouseY: Double): Boolean { + return rect.contains(mouseX, mouseY) + } } diff --git a/src/main/kotlin/impl/v1/FirmamentAPIImpl.kt b/src/main/kotlin/impl/v1/FirmamentAPIImpl.kt new file mode 100644 index 0000000..d5fc0de --- /dev/null +++ b/src/main/kotlin/impl/v1/FirmamentAPIImpl.kt @@ -0,0 +1,48 @@ +package moe.nea.firmament.impl.v1 + +import com.mojang.blaze3d.platform.InputConstants +import java.util.Collections +import java.util.Optional +import net.fabricmc.loader.api.FabricLoader +import kotlin.jvm.optionals.getOrNull +import moe.nea.firmament.Firmament +import moe.nea.firmament.api.v1.FirmamentAPI +import moe.nea.firmament.api.v1.FirmamentExtension +import moe.nea.firmament.api.v1.FirmamentItemWidget +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.intoOptional + +object FirmamentAPIImpl : FirmamentAPI() { + @JvmField + val INSTANCE: FirmamentAPI = FirmamentAPIImpl + + private val _extensions = mutableListOf<FirmamentExtension>() + override fun getExtensions(): List<FirmamentExtension> { + return Collections.unmodifiableList(_extensions) + } + + override fun getHoveredItemWidget(): Optional<FirmamentItemWidget> { + val mouse = MC.instance.mouseHandler + val window = MC.window + val xpos = mouse.getScaledXPos(window) + val ypos = mouse.getScaledYPos(window) + val widget = MC.screen + ?.getChildAt(xpos, ypos) + ?.getOrNull() + if (widget is FirmamentItemWidget) return widget.intoOptional() + return Optional.empty() + } + + fun loadExtensions() { + for (container in FabricLoader.getInstance() + .getEntrypointContainers("firmament:v1", FirmamentExtension::class.java)) { + Firmament.logger.info("Loading extension ${container.entrypoint} from ${container.provider.metadata.name}") + loadExtension(container.entrypoint) + } + extensions.forEach { it.onLoad() } + } + + fun loadExtension(entrypoint: FirmamentExtension) { + _extensions.add(entrypoint) + } +} diff --git a/src/main/kotlin/util/HoveredItemStack.kt b/src/main/kotlin/util/HoveredItemStack.kt index c8d33fd..91202dd 100644 --- a/src/main/kotlin/util/HoveredItemStack.kt +++ b/src/main/kotlin/util/HoveredItemStack.kt @@ -1,13 +1,16 @@ package moe.nea.firmament.util import com.google.auto.service.AutoService +import kotlin.jvm.optionals.getOrNull +import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.world.item.ItemStack +import moe.nea.firmament.api.v1.FirmamentAPI import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.util.compatloader.CompatLoader interface HoveredItemStackProvider : Comparable<HoveredItemStackProvider> { - fun provideHoveredItemStack(screen: AbstractContainerScreen<*>): ItemStack? + fun provideHoveredItemStack(screen: Screen): ItemStack? override fun compareTo(other: HoveredItemStackProvider): Int { return compareValues(this.prio, other.prio) } @@ -22,8 +25,8 @@ interface HoveredItemStackProvider : Comparable<HoveredItemStackProvider> { @AutoService(HoveredItemStackProvider::class) class VanillaScreenProvider : HoveredItemStackProvider { - override fun provideHoveredItemStack(screen: AbstractContainerScreen<*>): ItemStack? { - screen as AccessorHandledScreen + override fun provideHoveredItemStack(screen: Screen): ItemStack? { + if (screen !is AccessorHandledScreen) return null val vanillaSlot = screen.focusedSlot_Firmament?.item return vanillaSlot } @@ -32,7 +35,17 @@ class VanillaScreenProvider : HoveredItemStackProvider { get() = -1 } -val AbstractContainerScreen<*>.focusedItemStack: ItemStack? +@AutoService(HoveredItemStackProvider::class) +class FirmamentStackScreenProvider : HoveredItemStackProvider { + override fun provideHoveredItemStack(screen: Screen): ItemStack? { + return FirmamentAPI.getInstance() + .hoveredItemWidget + .getOrNull() + ?.itemStack + } +} + +val Screen.focusedItemStack: ItemStack? get() = HoveredItemStackProvider.sorted .firstNotNullOfOrNull { it.provideHoveredItemStack(this)?.takeIf { !it.isEmpty } } |
