Inventory Framework

repository·main·Indexed 41 days ago

https://github.com/devnatan/inventory-framework

A Minecraft Inventory API framework providing a high-level, secure, and robust way to create custom inventories with specialized interaction behaviors. It includes a Minestom platform implementation featuring RenderContext for item manipulation and a ViewFrame for managing view lifecycles, as well as an IntelliJ plugin for static analysis-based visual previews of inventory layouts.

Tokens
4.9K
Snippets
14
Records
23
Agent score
53%

What's inside Inventory Framework

  1. Overview of Inventory Framework

    main
    Inventory Framework is a Minecraft Inventory API framework designed to simplify the creation of custom inventories. It provides a high-level, secure, and robust API that allows developers to implement varied interaction treatments while automatically handling and resolving known platform-specific issues internally.
  2. How the IntelliJ Plugin preview works

    main

    The IntelliJ plugin provides a split-view editor where the source code is on the left and a visual preview of the inventory view is on the right.

    Key Characteristics:

    • Static Analysis Only: The preview is built using PSI/UAST (static analysis). It never compiles or runs your code. Consequently, anything depending on runtime state (non-literal variables, complex logic, or lambdas) will appear as a generic placeholder.
    • Live Refresh: The preview automatically re-extracts and repaints approximately 300ms after you finish an edit (debounced).
    • Dumb-mode Safe: If the project is still indexing, the plugin returns no match instead of erroring, and it will automatically retry once indexing is complete.
    • Editor Integration: It uses a single editor tab with a split view. You can use the standard top-right IntelliJ icons to switch between 'editor-only', 'split', or 'preview-only' modes.
  3. Branch naming conventions

    main

    The master branch is the only long-term branch. For short-term work, use the following naming patterns:

    • fix/something-needs-fix: Small routine patches for existing features.
    • feat/something-new: New features or changes to existing features (be mindful of breaking changes).
    • docs/improves-documentation-for-this-feature: Changes to documentation that do not impact source code.
    • chore/update-gradle-version: Updates to project files not related to source code.
  4. Define items in the preview using ItemExtractor

    main

    To see items in the preview, you must use patterns that the static analyzer can resolve. The analyzer looks for literal new ItemStack(Material.X) calls or simple local variable initializers.

    Supported Item Patterns:

    • Direct Slot Assignment: slot(index, item), slot(index).withItem(item).
    • Boundary Slots: firstSlot(item), lastSlot(item).
    • Layout Slots: layoutSlot(char, item).
    • Helper Fallbacks: If you pass a Material.X directly to a helper (e.g., withItem(ExampleUtil.displayItem(Material.STONE, "Label"))), the plugin uses the Material as a best-effort guess, even though it doesn't execute the helper.
    • Stack Counts: new ItemStack(Material.X, amount) renders a vanilla-style count badge if amount is a literal or a non-reassigned local variable.
    • Loop Support: Inside a simple bounded loop (e.g., for (int i = 1; i <= 5; i++)), the plugin can resolve the loop counter i to render incrementing stack counts (e.g., 1 through 5).
    // Example of what the plugin can resolve
    slot(0, new ItemStack(Material.DIAMOND, 64));
    
    // Example of loop-based stack counts
    for (int i = 1; i <= 5; i++) {
        render.availableSlot(new ItemStack(Material.GOLD_INGOT, i));
    }
  5. Git commit message standards

    main

    Commit messages should follow the seven rules of a great Git commit message to ensure clarity and maintainability:

    1. Separate the subject from the body with a blank line.
    2. Limit the subject line to 72 characters.
    3. Capitalize the subject line.
    4. Do not end the subject line with a period.
    5. Use the imperative mood in the subject line (e.g., "Add feature" instead of "Added feature").
    6. Wrap the body at 72 characters.
    7. Use the body to explain what and why, rather than how.

    Note: Vague messages like "fix tests" or "now it's working" will not be accepted. Additionally, always rebase your code to avoid reverse merge commits.

  6. Guidelines for writing code comments

    main

    Comments should focus on explaining the why behind decisions rather than describing the code's structure or obvious behavior.

    When to comment:

    • When a decision departs from common wisdom or convention (explain the reasoning).
    • When a code fragment required significant thought/research (e.g., if you spent >1 hour thinking about a fragment that took <2 minutes to write).
    • When you need to preserve specific implementation properties, such as performance-sensitive code, goroutine synchronization, security primitives, or congestion control algorithms.

    When NOT to comment:

    • On program structures that follow established conventions.
    • On pedantic explanations of behavior that are immediately obvious from the surrounding code.
    • On behavior you cannot personally attest to.
  7. Configure Minecraft icon paths in IntelliJ

    main

    The plugin attempts to render real item icons by reading model geometry directly from a local Minecraft client installation. If the plugin cannot auto-detect your Minecraft directory (e.g., if you use a portable launcher or a custom installation path), you can manually configure it.

    Setup: Go to Settings > Tools > Inventory Framework (MinecraftIconSettings) to override the .minecraft directory path.

  8. Initialize a ViewFrame

    main

    To use the inventory framework in Minestom, you must create and register a ViewFrame. The ViewFrame acts as the central controller for managing views and their lifecycles.

    1. Create the frame using ViewFrame.create(parentNode), passing in a Minestom EventNode (typically the server's main event node).
    2. Call .register() to initialize the framework, set up the element factory, and prepare the view pipeline.
    val viewFrame = ViewFrame.create(server.eventNode).register()
  9. Known limitations of the IntelliJ Plugin preview

    main

    Because the preview relies on static analysis rather than code execution, the following limitations apply:

    • No Dynamic Evaluation: Non-literal titles, renderWith/onRender lambdas, displayIf conditions, and complex item expressions will show as generic placeholders.
    • Heuristic-based Row/Column Filling: Calls to row(n), column(n), firstRow(), etc., use a "fill the whole row/column" heuristic. The plugin treats every call site as if it fills the entire row/column, which may not match runtime behavior if you intended to fill only a single slot.
    • availableSlot() Heuristics: The position of availableSlot() is estimated based on the layout and previously extracted slots. It does not simulate runtime interactability or complex nested loops.
    • resultSlot() is unsupported: Items placed via resultSlot() will not appear in the preview.
    • No Interaction: The preview is read-only; onClick and onSlotClick behaviors are not simulated.
    • Language Support: Limited to Java and Kotlin via UAST.
  10. Configure View properties for the preview

    main

    The ViewExtractor identifies several configuration properties to render the preview correctly:

    • ViewType: Detected via .type(ViewType.X) or the Views.rows(n) shorthand (which implies CHEST).
    • Size: Detected via .size(n) or Views.rows(n).
    • Title: Detected via .title("literal string").
    • Layout: Detected via the layout(...) char grid.
    • Slot Geometry: The plugin understands per-type slot geometry (rows/columns/max slot count) for various container types including CHEST, HOPPER, DROPPER, DISPENSER, FURNACE, BLAST_FURNACE, CRAFTING_TABLE, BREWING_STAND, BEACON, ANVIL, SHULKER_BOX, SMOKER, VILLAGER_TRADING, and PLAYER.
  11. Open a view to a player

    main

    Use the open methods on a ViewFrame instance to display an inventory (a View) to one or more players.

    • Single Player: Pass the View class and the Player object. You can optionally provide initialData to pass state to the view upon opening.
    • Multiple Players: Pass a collection of Player objects. These players will share the same inventory context and see the same state.
    • Existing Context: Use openActive to allow a player to join an already existing inventory context using its contextId.

    Note: Methods involving multiple players or active contexts are marked as @Experimental and may change in future releases.

    // Open a new view for a single player
    val contextId = viewFrame.open(MyView::class.java, player)
    
    // Open a new view for a single player with initial data
    viewFrame.open(MyView::class.java, player, myInitialData)
    
    // Open a new view for multiple players (Experimental)
    viewFrame.open(MyView::class.java, listOf(player1, player2))
    
    // Open an existing context to a player (Experimental)
    viewFrame.openActive(MyView::class.java, "existing-context-id", player)