Inventory Manager

Kore’s Inventory Manager lets you declaratively control inventories on entities or blocks and react to slot events.

  • Manage items in any ContainerArgument (players, entities, or block containers).
  • Register slot listeners that fire when an item is taken from a slot.
  • Keep a slot populated, clear other slots, or run custom logic every tick.
  • Auto-generate the required load/tick functions and minimal scoreboards to drive the listeners.

Quick start

import io.github.ayfri.kore.arguments.types.literals.nearestPlayer
import io.github.ayfri.kore.arguments.colors.Color
import io.github.ayfri.kore.arguments.chatcomponents.textComponent
import io.github.ayfri.kore.commands.TitleLocation
import io.github.ayfri.kore.generated.Items
import io.github.ayfri.kore.helpers.inventorymanager.inventoryManager

function("inventory_demo") {
    val playerInv = inventoryManager(nearestPlayer())

    playerInv.slotEvent(HOTBAR[0], Items.NETHER_STAR {
        this["display"] = nbt {
            this["Name"] = textComponent("Do not move me", color = Color.RED).toJsonString()
        }
    }) {
        onTake {
            title(self(), TitleLocation.ACTIONBAR, textComponent("Stop taking me!", color = Color.RED))
        }

        duringTake { setItemInSlot() }
        onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() }

        setItemInSlot() // seed the slot once
    }

    // Start listeners for this manager
    playerInv.generateSlotsListeners()
}
Kotlin

In game: taking the star out of the first hotbar slot shows the warning, and the star comes back

Tip: Use the builder variant to both declare listeners and auto-generate them in one go:

inventoryManager(nearestPlayer()) {
    slotEvent(HOTBAR[0], Items.DIAMOND) { setItemInSlot() }
    generateSlotsListeners()
}
Kotlin

Block containers

You can target block inventories as well by passing a Vec3 and (optionally) placing a container block first:

val chestPos = vec3(0, -59, 0)

inventoryManager(chestPos) {
    setBlock(Blocks.CHEST)

    slotEvent(CONTAINER[0], Items.DIAMOND_SWORD) {
        onTake { tellraw(allPlayers(), text("You took the sword!", Color.RED)) }
        duringTake { setItemInSlot() }
        onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() }
        setItemInSlot()
    }

    generateSlotsListeners()
}
Kotlin

API overview

  • inventoryManager(container) - Create a manager for any ContainerArgument (EntityArgument or Vec3). Internally uses /item to place items in slots.
  • slotEvent(slot, expectedItem) { … } - Register handlers for a single slot.
    • onTake { … } - Fires once when the slot transitions from expected item to something else.
    • duringTake { … } - Fires every tick while the slot is not holding the expected item; useful to enforce state.
    • onTick { … } - Runs every tick regardless of state; convenient for housekeeping.
    • Helpers inside the scope:
      • setItemInSlot() - Put back the expected item into the slot.
      • clearAllItemsNotInSlot([targets]) - Clear all other slots.
      • killAllItemsNotInSlot() - Remove dropped items that don’t belong to this slot.
  • generateSlotsListeners() - Emits the load/tick functions and scoreboard wiring for all registered listeners.
  • setBlock(block) - When the container is a position, place a block (e.g., a chest) before managing its contents.
  • clear(slot), clearAll(), clearAll(item) - Utilities to wipe inventory content.

Internally, Inventory Manager relies on a scoreboard objective and a tiny helper marker entity (for non-entity containers) to detect state transitions. Names are auto-namespaced and numbered in the order managers generate their listeners within the datapack, so the same pack always produces the same names.

Removing detectors

To clean up the objectives of every Inventory Manager the pack generated before this call:

dataPack("my_dp") {
    InventoryManager.removeClickDetectors()
}
Kotlin

Full example

This combines a player inventory policy with messaging and a chest that constantly re-seeds its first slot. It mirrors the test coverage used in Kore’s own suite.

fun Function.inventoryManagerTests() {
    val counter = "take_counter"
    val playerInv = inventoryManager(nearestPlayer())

    playerInv.slotEvent(HOTBAR[0], Items.NETHER_STAR) {
        onTake {
            title(self(), TitleLocation.ACTIONBAR, text("Don’t take me", Color.RED))
            scoreboard.players.add(self(), counter, 1)
        }
        duringTake { setItemInSlot() }
        onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() }
        setItemInSlot()
    }

    playerInv.generateSlotsListeners()

    datapack.load {
        scoreboard.objectives.add(counter)
        scoreboard.players.set(playerInv.container as ScoreHolderArgument, counter, 0)
    }

    inventoryManager(vec3(0, -59, 0)) {
        setBlock(Blocks.CHEST)
        slotEvent(CONTAINER[0], Items.DIAMOND_SWORD) {
            onTake { tellraw(allPlayers(), text("You took the diamond sword from the chest", Color.RED)) }
            duringTake { setItemInSlot() }
            onTick { clearAllItemsNotInSlot(); killAllItemsNotInSlot() }
            setItemInSlot()
        }
        generateSlotsListeners()
    }
}
Kotlin

See also

  • Scheduler - Run repeated or delayed logic that complements inventory policies.
  • Components - Define complex items (names, lore, enchantments) you can enforce in slots.
  • Predicates - Validate component-based item properties in other contexts.
  • Scoreboards - Background knowledge on objectives used under the hood.