# Table of Contents - [Setup | Wisp Forest Docs](#setup-wisp-forest-docs) - [Setup | Wisp Forest Docs](#setup-wisp-forest-docs) - [Defining new Forges though Data | Wisp Forest Docs](#defining-new-forges-though-data-wisp-forest-docs) - [Home | Wisp Forest Docs](#home-wisp-forest-docs) - [Creating a recipe | Wisp Forest Docs](#creating-a-recipe-wisp-forest-docs) - [Available API Events | Wisp Forest Docs](#available-api-events-wisp-forest-docs) - [API Fundamentals | Wisp Forest Docs](#api-fundamentals-wisp-forest-docs) - [Setup Environment | Wisp Forest Docs](#setup-environment-wisp-forest-docs) - [Rendering API Breakdown | Wisp Forest Docs](#rendering-api-breakdown-wisp-forest-docs) - [FAQ (Frequently Asked Questions) | Wisp Forest Docs](#faq-frequently-asked-questions-wisp-forest-docs) - [ItemStack Data Components | Wisp Forest Docs](#itemstack-data-components-wisp-forest-docs) - [Adjusting Accessory Equipablity | Wisp Forest Docs](#adjusting-accessory-equipablity-wisp-forest-docs) - [Slot Adjustments Methods | Wisp Forest Docs](#slot-adjustments-methods-wisp-forest-docs) - [Default Slots | Wisp Forest Docs](#default-slots-wisp-forest-docs) - [Binding Slots to Entities | Wisp Forest Docs](#binding-slots-to-entities-wisp-forest-docs) - [Creating Slot Groups | Wisp Forest Docs](#creating-slot-groups-wisp-forest-docs) - [Creating and Modifying Slots | Wisp Forest Docs](#creating-and-modifying-slots-wisp-forest-docs) - [Wisp Forest Docs](#wisp-forest-docs) - [Accessories | Wisp Forest Docs](#accessories-wisp-forest-docs) - [Recipe Adaptation | Wisp Forest Docs](#recipe-adaptation-wisp-forest-docs) - [Comparison to owo-ui | Wisp Forest Docs](#comparison-to-owo-ui-wisp-forest-docs) - [Wisp Forest Docs](#wisp-forest-docs) - [Json5 Data Loading | Wisp Forest Docs](#json5-data-loading-wisp-forest-docs) - [Using the option system for fun and profit | Wisp Forest Docs](#using-the-option-system-for-fun-and-profit-wisp-forest-docs) - [Nested Lang | Wisp Forest Docs](#nested-lang-wisp-forest-docs) - [Item Groups | Wisp Forest Docs](#item-groups-wisp-forest-docs) - [Endecs | Wisp Forest Docs](#endecs-wisp-forest-docs) - [Registration | Wisp Forest Docs](#registration-wisp-forest-docs) - [Networking | Wisp Forest Docs](#networking-wisp-forest-docs) - [Features | Wisp Forest Docs](#features-wisp-forest-docs) - [System Properties | Wisp Forest Docs](#system-properties-wisp-forest-docs) - [owo-ui Academy | Wisp Forest Docs](#owo-ui-academy-wisp-forest-docs) - [Button | Wisp Forest Docs](#button-wisp-forest-docs) - [Checkbox | Wisp Forest Docs](#checkbox-wisp-forest-docs) - [Setting up Hot Reloading | Wisp Forest Docs](#setting-up-hot-reloading-wisp-forest-docs) - [IntrinsicWidth and IntrinsicHeight | Wisp Forest Docs](#intrinsicwidth-and-intrinsicheight-wisp-forest-docs) - [Layout | Wisp Forest Docs](#layout-wisp-forest-docs) - [Collapsible Container | Wisp Forest Docs](#collapsible-container-wisp-forest-docs) - [Dropdown | Wisp Forest Docs](#dropdown-wisp-forest-docs) - [Flow Layout | Wisp Forest Docs](#flow-layout-wisp-forest-docs) - [owo-ui Components | Wisp Forest Docs](#owo-ui-components-wisp-forest-docs) - [Grid Layout | Wisp Forest Docs](#grid-layout-wisp-forest-docs) - [Label | Wisp Forest Docs](#label-wisp-forest-docs) - [Options | Wisp Forest Docs](#options-wisp-forest-docs) - [Scroll Container | Wisp Forest Docs](#scroll-container-wisp-forest-docs) - [Template Basics | Wisp Forest Docs](#template-basics-wisp-forest-docs) - [Slider | Wisp Forest Docs](#slider-wisp-forest-docs) - [/isorender | Wisp Forest Docs](#-isorender-wisp-forest-docs) - [Metadata Format | Wisp Forest Docs](#metadata-format-wisp-forest-docs) - [How to build a Forge | Wisp Forest Docs](#how-to-build-a-forge-wisp-forest-docs) - [Isometric Renders | Wisp Forest Docs](#isometric-renders-wisp-forest-docs) - [Numismatic Overhaul | Wisp Forest Docs](#numismatic-overhaul-wisp-forest-docs) - [Getting Started | Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Writing Extensions | Wisp Forest Docs](#writing-extensions-wisp-forest-docs) - [Markdown Syntax | Wisp Forest Docs](#markdown-syntax-wisp-forest-docs) - [Shop | Wisp Forest Docs](#shop-wisp-forest-docs) - [Villager Trade Data Format | Wisp Forest Docs](#villager-trade-data-format-wisp-forest-docs) - [Getting Started | Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Annotations | Wisp Forest Docs](#annotations-wisp-forest-docs) - [Structures | Wisp Forest Docs](#structures-wisp-forest-docs) - [Config | Wisp Forest Docs](#config-wisp-forest-docs) - [Constraints | Wisp Forest Docs](#constraints-wisp-forest-docs) - [Getting Started | Wisp Forest Docs](#getting-started-wisp-forest-docs) - [owo-ui | Wisp Forest Docs](#owo-ui-wisp-forest-docs) - [Synchronization | Wisp Forest Docs](#synchronization-wisp-forest-docs) - [Layout Basics | Wisp Forest Docs](#layout-basics-wisp-forest-docs) - [Getting Started | Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Rich Translations | Wisp Forest Docs](#rich-translations-wisp-forest-docs) - [Recipe Remainders | Wisp Forest Docs](#recipe-remainders-wisp-forest-docs) - [Component Basics | Wisp Forest Docs](#component-basics-wisp-forest-docs) - [Utility Components | Wisp Forest Docs](#utility-components-wisp-forest-docs) - [RenderDoc Integration | Wisp Forest Docs](#renderdoc-integration-wisp-forest-docs) - [owo-ui | Wisp Forest Docs](#owo-ui-wisp-forest-docs) - [owo-ui Components | Wisp Forest Docs](#owo-ui-components-wisp-forest-docs) - [Config | Wisp Forest Docs](#config-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Setup - Wisp Forest Docs](#setup-wisp-forest-docs) - [Setup - Wisp Forest Docs](#setup-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Fabric API Events - Wisp Forest Docs](#fabric-api-events-wisp-forest-docs) - [/isorender - Wisp Forest Docs](#-isorender-wisp-forest-docs) - [Limelight - Wisp Forest Docs](#limelight-wisp-forest-docs) - [FAQ - Wisp Forest Docs](#faq-wisp-forest-docs) - [Villager Trade Data Format - Wisp Forest Docs](#villager-trade-data-format-wisp-forest-docs) - [Features - Wisp Forest Docs](#features-wisp-forest-docs) - [Item Groups - Wisp Forest Docs](#item-groups-wisp-forest-docs) - [Networking - Wisp Forest Docs](#networking-wisp-forest-docs) - [Registration - Wisp Forest Docs](#registration-wisp-forest-docs) - [Endecs - Wisp Forest Docs](#endecs-wisp-forest-docs) - [System Properties - Wisp Forest Docs](#system-properties-wisp-forest-docs) - [Config - Wisp Forest Docs](#config-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Home - Wisp Forest Docs](#home-wisp-forest-docs) - [Datapack Tutorial - Wisp Forest Docs](#datapack-tutorial-wisp-forest-docs) - [Options - Wisp Forest Docs](#options-wisp-forest-docs) - [Options - Wisp Forest Docs](#options-wisp-forest-docs) - [Shops - Wisp Forest Docs](#shops-wisp-forest-docs) - [Recipe Remainders - Wisp Forest Docs](#recipe-remainders-wisp-forest-docs) - [Rich Translations - Wisp Forest Docs](#rich-translations-wisp-forest-docs) - [RenderDoc Integration - Wisp Forest Docs](#renderdoc-integration-wisp-forest-docs) - [Getting Started - Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Annotations - Wisp Forest Docs](#annotations-wisp-forest-docs) - [Constraints - Wisp Forest Docs](#constraints-wisp-forest-docs) - [owo-ui - Wisp Forest Docs](#owo-ui-wisp-forest-docs) - [Synchronization - Wisp Forest Docs](#synchronization-wisp-forest-docs) - [owo-ui Academy - Wisp Forest Docs](#owo-ui-academy-wisp-forest-docs) - [Component Basics - Wisp Forest Docs](#component-basics-wisp-forest-docs) - [Layout Basics - Wisp Forest Docs](#layout-basics-wisp-forest-docs) - [Getting Started - Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Utility Components - Wisp Forest Docs](#utility-components-wisp-forest-docs) - [Button - Wisp Forest Docs](#button-wisp-forest-docs) - [owo-ui Components - Wisp Forest Docs](#owo-ui-components-wisp-forest-docs) - [Collapsible Container - Wisp Forest Docs](#collapsible-container-wisp-forest-docs) - [Checkbox - Wisp Forest Docs](#checkbox-wisp-forest-docs) - [Flow Layout - Wisp Forest Docs](#flow-layout-wisp-forest-docs) - [Dropdown - Wisp Forest Docs](#dropdown-wisp-forest-docs) - [Label - Wisp Forest Docs](#label-wisp-forest-docs) - [Slider - Wisp Forest Docs](#slider-wisp-forest-docs) - [Grid Layout - Wisp Forest Docs](#grid-layout-wisp-forest-docs) - [Templates - Wisp Forest Docs](#templates-wisp-forest-docs) - [Scroll Container - Wisp Forest Docs](#scroll-container-wisp-forest-docs) - [Getting Started - Wisp Forest Docs](#getting-started-wisp-forest-docs) - [Writing Extensions - Wisp Forest Docs](#writing-extensions-wisp-forest-docs) - [Metadata Format - Wisp Forest Docs](#metadata-format-wisp-forest-docs) - [scatter upload - Wisp Forest Docs](#scatter-upload-wisp-forest-docs) - [scatter edit - Wisp Forest Docs](#scatter-edit-wisp-forest-docs) - [Structures - Wisp Forest Docs](#structures-wisp-forest-docs) - [Server Owners and Operators - Wisp Forest Docs](#server-owners-and-operators-wisp-forest-docs) - [scatter config - Wisp Forest Docs](#scatter-config-wisp-forest-docs) - [Packet Dumping - Wisp Forest Docs](#packet-dumping-wisp-forest-docs) - [Entity Inspector - Wisp Forest Docs](#entity-inspector-wisp-forest-docs) - [Markdown Syntax - Wisp Forest Docs](#markdown-syntax-wisp-forest-docs) - [Quick Start - Wisp Forest Docs](#quick-start-wisp-forest-docs) - [Defining new Forges though Data - Wisp Forest Docs](#defining-new-forges-though-data-wisp-forest-docs) - [NBT Inspector - Wisp Forest Docs](#nbt-inspector-wisp-forest-docs) - [Adjusting Accessory Equipablity - Wisp Forest Docs](#adjusting-accessory-equipablity-wisp-forest-docs) - [Recipe Adaptation - Wisp Forest Docs](#recipe-adaptation-wisp-forest-docs) - [Default Slots - Wisp Forest Docs](#default-slots-wisp-forest-docs) - [Creating and Modifying Slots - Wisp Forest Docs](#creating-and-modifying-slots-wisp-forest-docs) - [Adding Recipes and Fuels - Wisp Forest Docs](#adding-recipes-and-fuels-wisp-forest-docs) - [FAQ (Frequently Asked Questions) - Wisp Forest Docs](#faq-frequently-asked-questions-wisp-forest-docs) - [Binding Slots to Entities - Wisp Forest Docs](#binding-slots-to-entities-wisp-forest-docs) - [Creating Slot Groups - Wisp Forest Docs](#creating-slot-groups-wisp-forest-docs) - [Adding support for Custom Packets - Wisp Forest Docs](#adding-support-for-custom-packets-wisp-forest-docs) - [Adjusting Slot Amount - Wisp Forest Docs](#adjusting-slot-amount-wisp-forest-docs) - [Rendering API Breakdown - Wisp Forest Docs](#rendering-api-breakdown-wisp-forest-docs) - [Setup Environment - Wisp Forest Docs](#setup-environment-wisp-forest-docs) - [API Fundamentals - Wisp Forest Docs](#api-fundamentals-wisp-forest-docs) - [Setup - Wisp Forest Docs](#setup-wisp-forest-docs) - [Defining Extensions - Wisp Forest Docs](#defining-extensions-wisp-forest-docs) - [Adding Wikis - Wisp Forest Docs](#adding-wikis-wisp-forest-docs) - [Available API Events - Wisp Forest Docs](#available-api-events-wisp-forest-docs) - [Adding Wiki Sources - Wisp Forest Docs](#adding-wiki-sources-wisp-forest-docs) - [Providing Results - Wisp Forest Docs](#providing-results-wisp-forest-docs) - [ItemStack Data Components - Wisp Forest Docs](#itemstack-data-components-wisp-forest-docs) - [How to build a Forge - Wisp Forest Docs](#how-to-build-a-forge-wisp-forest-docs) --- # Setup | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/setup#VPContent) Return to top Setup [​](https://docs.wispforest.io/owo/setup#setup) ====================================================== To add oωo to you project, begin by including our maven in the repositories block of your `build.gradle` groovy repositories { maven { url 'https://maven.wispforest.io/releases/' } // since owo 0.13.0, jitpack is required for kdl4j maven { url 'https://jitpack.io' } } Then, declare the dependency inside your `dependencies` block and as well as the version you want to use inside your `gradle.properties`. build.gradle (Fabric)build.gradle (Neoforge)build.gradle (Common)gradle.properties groovy dependencies { modImplementation "io.wispforest:owo-lib:${project.owo_version}" include "io.wispforest:owo-sentinel:${project.owo_version}" } groovy dependencies { // Moddev Projects - Neoforge implementation "io.wispforest:owo-lib-neoforge:${project.owo_version}" accessTransformer "io.wispforest:owo-lib-neoforge:${project.owo_version}" interfaceInjectionData "io.wispforest:owo-lib-neoforge:${project.owo_version}" // Arch Loom Projects - Neoforge modImplementation "io.wispforest:owo-lib-neoforge:${project.owo_version}" // Required due to issues with Arch Loom and JIJ within neo. May require bumping the version every once and awhile. forgeRuntimeLibrary("blue.endless:jankson:1.2.2") // For versions greater than or equal to 1.21.4 forgeRuntimeLibrary("io.wispforest:endec:0.1.9") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.5") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.6") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.6") // For versions less than or equal to 1.21.1 forgeRuntimeLibrary("io.wispforest:endec:0.1.5.1") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.2") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.3.1") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.3.1") } groovy dependencies { // Moddev Projects - Neoforge compileOnly "io.wispforest:owo-lib-neoforge:${project.owo_version}" accessTransformer "io.wispforest:owo-lib-neoforge:${project.owo_version}" interfaceInjectionData "io.wispforest:owo-lib-neoforge:${project.owo_version}" // Arch Loom Projects - Neoforge // Don't worry about loading issues as it will only be present to get the arch interface injection and Access Widener modImplementation "io.wispforest:owo-lib-fabric:${project.owo_version}" } properties # https://maven.wispforest.io/io/wispforest/owo-lib/ owo_version=0.13.0+26.1 INFO As you can see, this example also includes `owo-sentinel`. sentinel is a super tiny mod which is designed to be Jar-in-Jar'd by mods that depend on owo. If a player then installs your mod without installing owo, sentinel will prevent their game from launching and instead open a window warning them that owo is required. It gives them the option to automatically install owo or open owo's page so they can do it manually. **Important:** owo-sentinel _never_ does anything without user consent. It has a built-in explanation and only establishes a network connection or modifies files on disk after the user explicitly requests it with a clearly labeled button. owo-sentinel dependency declaration Even when using owo-sentinel, it is still necessary and **very important** that you correctly declare your dependency on owo in _both_ your mod's `fabric.mod.json` and on whichever distribution platforms you use. If you don't do this, you **will** cause for other developers and players. Launchers will not be able to correctly instally owo as a dependency, mod bisection tools will not detect your owo dependency and trigger an uncesssary sentinel launch, and so on and so forth. owo-sentinel is meant to be a _last-resort_ convenience option for users who install your mod without using a launcher or similar tool which takes care of installing the owo dependency. It does not absolve you of the responsibility to manage your dependencies correctly. If you want to use a version other than the most current one, check the [GitHub releases page](https://github.com/wisp-forest/owo-lib/releases/) --- # Setup | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/lavender/setup#VPContent) Return to top Setup [​](https://docs.wispforest.io/lavender/setup#setup) =========================================================== Work-in-progress The Lavender documentation is still being written and not quite complete yet. In places where an article here is still missing, feel free to refer to Lavender's Javadoc where present or simply ask for help on [our Discord](https://discord.gg/xrwHKktV2d) To add Lavender to you project, begin by including our maven in the repositories block of your `build.gradle` build.gradle groovy repositories { maven { url 'https://maven.wispforest.io' } } Then, declare the dependency inside your `dependencies` block and as well as the version you want to use inside your `gradle.properties`. Since Lavender depends on owo-lib, it's generally recommended to also include owo-sentinel as a means for your players to easily acquire the library without having to download it themselves. Check out [owo's setup page](https://docs.wispforest.io/owo/setup) for more information build.gradlegradle.properties groovy dependencies { modImplementation "io.wispforest:lavender:${project.lavender_version}" include "io.wispforest:owo-sentinel:${project.owo_version}" // check owo's page for this version } properties # https://maven.wispforest.io/io/wispforest/lavender/ lavender_version=... If you want to use a version other than the most current one, check the [GitHub releases page](https://github.com/wisp-forest/lavender/releases/) --- # Defining new Forges though Data | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/alloy-forgery/defining-a-forge#VPContent) On this page Defining new Forges though Data [​](https://docs.wispforest.io/alloy-forgery/defining-a-forge#defining-new-forges-though-data) =============================================================================================================================== Where do I define it? [​](https://docs.wispforest.io/alloy-forgery/defining-a-forge#where-do-i-define-it) ---------------------------------------------------------------------------------------------------------- When Alloy Forgery is loaded for the first time it generates a folder in the `.minecraft` directory, which is called `moddata`. Under the `moddata` folder you can create new forges, and override existing ones. An example would be overriding the deepslate forge. For this we would create enough folders so that we can go into `moddata/alloy_forgery/alloy_forges`, and create a new `deepslate_bricks_forge.json` file. This new file _will override_ the existing deepslate bricks forge, allowing us to change it. For creating new forges simply create your own data folder. An example would be `moddata/custom_content/alloy_forges`, with a new `bedrock_forge.json` file inside. Defining the forge [​](https://docs.wispforest.io/alloy-forgery/defining-a-forge#defining-the-forge) ----------------------------------------------------------------------------------------------------- An example is provided below: JSON { "material": "minecraft:deepslate_bricks", "additional_materials": [\ "minecraft:deepslate_tiles",\ "minecraft:polished_deepslate",\ "minecraft:chiseled_deepslate"\ ], "tier": 2, "speed_multiplier": 1.5, "fuel_capacity": 96000 } | Field | Description | | --- | --- | | `material` | Defines the block used for crafting the Forge Controller and the block the Forge is built from. | | `additional_materials` | Defines extra materials the Forge structure can be built from. | | `tier` | The tier of the Forge. Determines what recipes it can process. | | `fuel_capacity` | How much fuel the Forge can hold before needing to be refueled. | | `speed_multiplier` | A multiplier which decides how fast the Forge processes a recipe. Does not impact fuel consumption. | --- # Home | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/alloy-forgery/home#VPContent) Return to top Alloy Forgery [​](https://docs.wispforest.io/alloy-forgery/home#alloy-forgery) =============================================================================== Alloy Forgery is a mod all about forging metals. If you are a player looking for info on how to make an Alloy Forge, see the ["Building a Forge"](https://docs.wispforest.io/alloy-forgery/building-a-forge) section. We recommend a mod like Roughly Enough Items or EMI to view alloy recipes. By default, Alloy Forgery provides no new materials, but it does provide recipes for smelting ores into extra ingots. It also supports a handful of mods out of the box, [which you can see here.](https://github.com/wisp-forest/alloy-forgery/tree/1.20/src/main/resources/data/alloy_forgery/recipes/compat) Furthermore, if you need any assistance on finding out how to create a Datapack in general, you can see Datapack Tutorial for help with such. --- # Creating a recipe | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#VPContent) On this page Creating a recipe [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#creating-a-recipe) =========================================================================================================== When creating an Alloy Forge recipe you want to use the `alloy_forgery:forging` type. The recipe can be placed anywhere under `data//recipes` in your data. Here is an example of a recipe: copper\_ingots\_from\_ore.json JSON { "type": "alloy_forgery:forging", "inputs": [\ {\ "tag": "minecraft:copper_ores"\ }\ ], "output": { "item": "minecraft:copper_ingot", "count": 3 }, "overrides": { "2": { "item": "minecraft:copper_ingot", "count": 4 }, "3+": { "item": "minecraft:copper_ingot", "count": 5 } }, "min_forge_tier": 1, "fuel_per_tick": 5 } Input Format [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#input-format) ------------------------------------------------------------------------------------------------- The inputs follow the Minecraft ingredient format (used by most other recipes) with such being the identifier of a given item tag (`"tag": ...`) or entry (`"item": ...`) and an additional `count` field used to give info about how much of such is required. A single recipe can accept, at max, **10** different ingredient entries. Furthermore, we also support any of Fabric's custom Ingredients like its Custom NBT-based Ingredient. Output Format [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#output-format) --------------------------------------------------------------------------------------------------- There currently exist two methods of returning an item as an output: ### Item [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#item) Using the `item` field as the item's identifier combined with a `count` for control on the stack size of such returned item. Such is shown above as the first example. ### Tagged 2.0.16+ [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#tagged) Using the `default` field supplied with the desired target tag will allow for whichever entries within such to be used as an output though due to tags random ordering, it's best to supply a `priority` array of outputs to chose from that are known to work out the gate if found. Tag Output Selection The `priority` array will be sequentially checked to see if the specified entry exists and if none are found, will default to using the tag to try and find any entry to use as an output. lead\_ingots\_from\_raw\_ore.json JSON { "fabric:load_conditions": [\ {\ "condition": "fabric:item_tags_populated",\ "values": [\ "c:raw_lead_ores",\ "c:lead_ingots"\ ]\ }\ ], "type": "alloy_forgery:forging", "inputs": [\ {\ "tag": "c:raw_lead_ores",\ "count": 2\ }\ ], "output": { "priority": [\ "techreborn:lead_ingot",\ "indrev:lead_ingot",\ "modern_industrialization:lead_ingot"\ ], "default": "c:lead_ingots", "count": 3 }, "overrides": { "2+": { "count": 4 } }, "min_forge_tier": 1, "fuel_per_tick": 5 } ### Overrides [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#overrides) Overrides allow changing the output item depending on the tier of the Forge. Accepted formatting: | Examples | Description | | --- | --- | | `"2"` | The override applies only to tier 2. | | `"3+"` | The override applies to tier 3, and anything above it. | | `"2 to 5"` | The override only applies to the specified range of tiers, in this case from tier 2 to tier 5. | ### Minimum Forge Tier [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#minimum-forge-tier) `min_forge_tier` field indicates the minimum tier required to use this recipe. ### Fuel Per Tick [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#fuel-per-tick) `fuel_per_tick` field indicates the amount of fuel consumed by the Forge per tick. One bucket of lava is 24000 fuel. Recipe Remainders [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#recipe-remainders) ----------------------------------------------------------------------------------------------------------- 2.0.17+ and owo-lib 0.8.0+ Due to Vanilla's very generic recipe remainders, you can use owo's [Recipe Specific Remainders](https://docs.wispforest.io/owo/data-extensions/recipe-remainders) instead, allowing for fully customizable recipe remainders, or use the built-in global remainder system loaded through the `data//forge_remainder` folder. A example for global Alloy Forgery remainders is below: JSON { "remainders": { // Count is supported "minecraft:iron_ore": { "item": "minecraft:cobblestone", "count": 4 }, // Turn copper in input to sand "minecraft:copper_ore": "minecraft:sand", } } In the upper example you can declare a map of your item to an object. The initial Item ID here is the input to be replaced. Supported settings: | Field | Description | | --- | --- | | `item` | The ID of the returned item. | | `count` | Custom stack counts (optional) | In the lower example, the initial item (copper ore) is what is being checked for, and the second item (sand) is returned once the recipe completes. Adding new fuels [​](https://docs.wispforest.io/alloy-forgery/adding-recipes-and-fuels#adding-new-fuels) ========================================================================================================= Alloy Forgery loads fuel from a specific folder in data. The path is `data//alloy_forge_fuels`, and in here you put your fuel definition. A fuel file does not require a specific name, and can hold multiple different fuels. Currently, we only support items for fuels, tags are not accepted. An example is provided below: JSON { "fuels": [\ {\ "item": "minecraft:lava_bucket",\ "return_item": "minecraft:bucket",\ "fuel": 24000\ },\ {\ "item": "minecraft:coal",\ "fuel": 1000\ },\ {\ "item": "minecraft:charcoal",\ "fuel": 1000\ },\ {\ "item": "minecraft:blaze_rod",\ "fuel": 2000\ },\ {\ "item": "minecraft:coal_block",\ "fuel": 9000\ }\ ] } The special field `return_item` is optional, and is intended for when you want to do something similar to returning an empty bucket after using a lava bucket with the forge. --- # Available API Events | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/developer/api_events#VPContent) On this page Available API Events [​](https://docs.wispforest.io/accessories/developer/api_events#available-api-events) =========================================================================================================== ### [AccessoryChangeCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/AccessoryChangeCallback.java#L15) [​](https://docs.wispforest.io/accessories/developer/api_events#accessorychangecallback) An event which fires when a change for a slot occurs. The event provides the current stack, and the previous stack combined with the type of slot change which occurred. That change can either be `MUTATION` or `REPLACEMENT`. * `MUTATION` means that the stack was not unequipped but the NBT data was modified. Usually it is still the same item. * `REPLACEMENT` indicates a `setStack()` call has occurred on the inventory, leading to the stack being entirely different (usually or always?). ### [AdjustAttributeModifierCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/AdjustAttributeModifierCallback.java#L16) [​](https://docs.wispforest.io/accessories/developer/api_events#adjustattributemodifiercallback) An event used to adjust the attributes of the accessory stack. This is collected from the [DataComponent method](https://github.com/wisp-forest/accessories/blob/a549372f7b6ac9a367d1b49a821759528cadce24/common/src/main/java/io/wispforest/accessories/api/components/AccessoryItemAttributeModifiers.java#L45) or from the [`Accessory#getModifiers`](https://github.com/wisp-forest/accessories/blob/a549372f7b6ac9a367d1b49a821759528cadce24/common/src/main/java/io/wispforest/accessories/api/Accessory.java#L81) call. ### [AllowEntityModificationCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/AllowEntityModificationCallback.java#L22) [​](https://docs.wispforest.io/accessories/developer/api_events#allowentitymodificationcallback) An event used for allowing or restricting access for an entity to modify various aspects of Accessories. Some examples include opening the screen, adjusting accessories, or toggling the visibility of the rendering. ### [CanEquipCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/CanEquipCallback.java#L21) [​](https://docs.wispforest.io/accessories/developer/api_events#canequipcallback) An event used to adjust if the accessory is equippable based on the `ItemStack` and `SlotReference` ### [CanUnequipCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/CanUnequipCallback.java#L15) [​](https://docs.wispforest.io/accessories/developer/api_events#canunequipcallback) An event used to adjust if the accessory can be unequipped based on the `ItemStack` and `SlotReference` ### [ContainersChangeCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/ContainersChangeCallback.java#L17C18-L17C43) [​](https://docs.wispforest.io/accessories/developer/api_events#containerschangecallback) An event fired when a change occurs for the [AccessoryContainer](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/AccessoriesContainer.java#L13) . This happens before the changes are synced. It provides a map of changed containers (`AccessoriesContainer`s) to whether the change was related to resizing (boolean). ### [OnDropCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/OnDropCallback.java#L19) [​](https://docs.wispforest.io/accessories/developer/api_events#ondropcallback) An event used to adjust the drop rule for the stack when such is being handled after the of the target entity found within [`AccessoriesEventHandler#onDeath`](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/impl/AccessoriesEventHandler.java#L564) . ### [OnDeathCallback](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/OnDeathCallback.java#L18) [​](https://docs.wispforest.io/accessories/developer/api_events#ondeathcallback) An event fired after an entity dies, and after all `OnDropCallback` have been calculated. This can be used to cancel default dropping behavior, or adjust stack data before dropping the items. Implemented Events [​](https://docs.wispforest.io/accessories/developer/api_events#implemented-events) ------------------------------------------------------------------------------------------------------- Any event below can be implemented directly on an `Accessory` implementation or by registering to the event object within the respective interface. ### [AllowWalkingOnSnow](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/extra/AllowWalkingOnSnow.java#L20) [​](https://docs.wispforest.io/accessories/developer/api_events#allowwalkingonsnow) An event allowing the ability to adjust if the entity should be able to walk on powdered snow. ### [IsGazeDisguised](https://github.com/wisp-forest/accessories/blob/c41504c63f5c608e1e0ea249fae8b1a152c92f29/common/src/main/java/io/wispforest/accessories/api/events/extra/IsGazeDisguised.java#L20) [​](https://docs.wispforest.io/accessories/developer/api_events#isgazedisguised) An event allowing for the control over if the given looking `LivingEntity` sees the wearer entity as disguised to prevent certain actions like being angered or allowing the creature to move. ### [FortuneAdjustment](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/extra/FortuneAdjustment.java#L18) [​](https://docs.wispforest.io/accessories/developer/api_events#fortuneadjustment) An event allowing for the adjustment of the level of fortune provided to the entity when calculating for such. ### [LootingAdjustment](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/extra/LootingAdjustment.java#L17) [​](https://docs.wispforest.io/accessories/developer/api_events#lootingadjustment) An event allowing for the adjustment of the level of looting provided to the entity when calculating for on the death of an attacked entity. ### [PiglinNeutralInducer](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/extra/PiglinNeutralInducer.java#L19) [​](https://docs.wispforest.io/accessories/developer/api_events#piglinneutralinducer) An event allowing for adjusting if the entity should anger any piglins within range of them. **Deprecated** [​](https://docs.wispforest.io/accessories/developer/api_events#deprecated) ------------------------------------------------------------------------------------------- ### [EndermanMasked](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/events/extra/EndermanMasked.java#L20) [​](https://docs.wispforest.io/accessories/developer/api_events#endermanmasked) Recommend switching to `IsGazeDisguised` event instead and specifically checking for Enderman as the looker entity. --- # API Fundamentals | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/developer/api_fundamentals#VPContent) On this page API Fundamentals [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#api-fundamentals) ========================================================================================================= ### Creating an Accessory [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#creating-an-accessory) You can either create an Accessory by implementing the `Accessory` interface or by extending the `AccessoryItem` which implements the `Accessory` interface. The first requires that you register your custom `Accessory` to the given `Item` using the `AccessoryRegistry#register` method. Using the `AccessoryItem` class will automatically register the accessory when the class is instantiated. This `Accessory` interface contains various functions that can be performed. Some examples include `tick`ing, controlling equipability, listening to when equipment is changed, adding attribute modifiers (dynamically or statically), and adding custom tooltip info. ### Adding a Custom Accessory Renderer [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#adding-a-custom-accessory-renderer) Creating a custom `Accessory` comes with the ability to add some rendering that is attached to the player. You can do so by creating a custom instance of `AccessoryRenderer` and registering it using the `AccessoriesRendererRegistry.registerRenderer` method. More information about the rendering API be found in the [rendering API section](https://docs.wispforest.io/accessories/developer/rendering_api) . Default Rendering Behavior By default within the Rendering API: all Accessories will use `DefaultAccessoryRenderer`, unless a custom `AccessoryRenderer` is registered, or an explicit "no renderer" is registered using `AccessoriesRendererRegistry#registerNoRenderer`. ### Creating a custom Slot Predicate [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#creating-a-custom-slot-predicate) In cases where neither a Tag or using Data Components on your Item Stack are suitable, you may create a custom `SlotBasedPredicate` where you can have more control over what `Accessory` entries are allowed to be equipped in a given slot. This is useful for dynamic accessory types that may have multiple iterations that would make it difficult to use a Item Tag without possible issues. !!! info "Entity Based Predicate" With the release of 1.2.0, `EntityBasedPredicate` exists as a way to have entity context when evaluating if a given Accessory should be equipped within a given slot. It is recommend that this is used only if needed, as it can be difficult to evaluate for cases like crafting where the target entity may differ. ### Accessing The Capability [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#accessing-the-capability) Within Accessories there is the `AccessoriesCapability` which is used to access the slot containers, adjust slot attributes, or query the inventory for various possible item stacks. Any interaction involving a given entity will be exposed in its `AccessoriesCapability`, which only exists if the entity has slots bound it. Make sure to write your logic to respect the fact that not every entity has slots bound to it (since the capability could be `null`). ### Adjusting Accessory Equipping [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#adjusting-accessory-equipping) A considerable difference between other Accessory API's is that `canEquip` is checked when Accessory data is loaded, and revalidated on reload. This requires you to use `isAnotherEquipped` found on the given entities `AccessoriesCapability` to prevent duplicate Accessory items from being equipped. This requires the current `ItemStack` and its `SlotReference` to be ignored when checking for another equipped accessory. ### Dealing with the Container [​](https://docs.wispforest.io/accessories/developer/api_fundamentals#dealing-with-the-container) For each slot on an entity - there will be an `AccessoriesContainer` instance if the given Entity has the slot bound to it. Bear in mind that when checking this, the slot you specify may not exist on the entity (it can be `null`). This object allows for getting information like the current equipped accessories, render toggles for given accessories and managing the slot attributes stored. It mainly not recommend to directly use the container to look for Accessories but instead use the methods on `AccessoriesCapability` instead. --- # Setup Environment | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/developer/dev_setup#VPContent) On this page Setup Environment [​](https://docs.wispforest.io/accessories/developer/dev_setup#setup-environment) ==================================================================================================== Accessories supports both **Fabric** and **Neoforge**, and also support mods that are multiplatform by offering a **Common** target. Groovy [​](https://docs.wispforest.io/accessories/developer/dev_setup#groovy) ------------------------------------------------------------------------------ To start you will need to add the following mavens to your `build.gradle`: build.gradle groovy maven { url 'https://maven.wispforest.io/releases' } maven { url 'https://maven.su5ed.dev/releases' } maven { url 'https://maven.fabricmc.net' } After declaring such, you will need to add the dependency within the `dependencies` block while also specifying the `accessories_version` within your `gradle.properties`: FabricNeoforge groovy dependencies { modImplementation("io.wispforest:accessories-fabric:${project.accessories_version}") } groovy dependencies { implementation("io.wispforest:accessories-neoforge:${project.accessories_version}") } #### Multiloader [​](https://docs.wispforest.io/accessories/developer/dev_setup#multiloader) Common - Arch LoomNeoforge - Arch LoomCommon - Vanilla Gradle groovy dependencies { modImplementation("io.wispforest:accessories-common:${project.accessories_version}") } groovy dependencies { modImplementation("io.wispforest:accessories-neoforge:${project.accessories_version}") // Required due to issues with JIJ dependency resolving in arch or something forgeRuntimeLibrary("blue.endless:jankson:1.2.2") // For versions greater than or equal to 1.21.4 forgeRuntimeLibrary("io.wispforest:endec:0.1.9") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.6") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.6") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.5") // For versions less than or equal to 1.21.1 forgeRuntimeLibrary("io.wispforest:endec:0.1.5.1") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.3.1") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.3.1") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.2") } groovy dependencies { // Yarn Intermediary compileOnly("io.wispforest:accessories-common:${project.accessories_version}") // Mojang Mappings compileOnly("io.wispforest:accessories-common:${project.accessories_version}-mojmap") } Kotlin [​](https://docs.wispforest.io/accessories/developer/dev_setup#kotlin) ------------------------------------------------------------------------------ To start you will need to add the following mavens to your `build.gradle`: build.gradle.kts kotlin maven("https://maven.wispforest.io/releases") maven("https://maven.su5ed.dev/releases") maven("https://maven.fabricmc.net") After declaring such, you will need to add the dependency within the `dependencies` block while also specifying the `accessories_version` within your `gradle.properties`: FabricNeoforge kotlin dependencies { modImplementation("io.wispforest:accessories-fabric:${properties["accessories_version"]}") } kotlin dependencies { implementation("io.wispforest:accessories-neoforge:${properties["accessories_version"]}") } #### Multiloader [​](https://docs.wispforest.io/accessories/developer/dev_setup#multiloader-1) Common - Arch LoomNeoforge - Arch LoomCommon - Vanilla Gradle kotlin dependencies { modImplementation("io.wispforest:accessories-common:${properties["accessories_version"]}") } kotlin dependencies { modImplementation("io.wispforest:accessories-neoforge:${properties["accessories_version"]}") // Required due to issues with JIJ dependency resolving in arch or something forgeRuntimeLibrary("blue.endless:jankson:1.2.2") // For versions greater than or equal to 1.21.4 forgeRuntimeLibrary("io.wispforest:endec:0.1.9") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.6") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.6") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.5") // For versions less than or equal to 1.21.1 forgeRuntimeLibrary("io.wispforest:endec:0.1.5.1") forgeRuntimeLibrary("io.wispforest.endec:gson:0.1.3.1") forgeRuntimeLibrary("io.wispforest.endec:jankson:0.1.3.1") forgeRuntimeLibrary("io.wispforest.endec:netty:0.1.2") } kotlin dependencies { // Yarn Intermediary compileOnly("io.wispforest:accessories-common:${properties["accessories_version"]}") // Mojang Mappings compileOnly("io.wispforest:accessories-common:${properties["accessories_version"]}-mojmap") } TIP It is recommended to get the latest version either from Modrinth, Curseforge or check the [Maven](https://maven.wispforest.io/#/releases/io/wispforest/accessories-common) for latest dev builds. --- # Rendering API Breakdown | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/developer/rendering_api#VPContent) On this page Rendering API Breakdown [​](https://docs.wispforest.io/accessories/developer/rendering_api#rendering-api-breakdown) ==================================================================================================================== To link a given item to a specific `AccessoryRenderer` you will need to register such within the `AccessoriesRendererRegistry` class. By **default** all accessory items that have no renderer registered will get the `DefaultAccessoryRenderer` which attempts to render the block/item on the player in a position where the slot is located on the entity. Default Accessory Rendering Behavior All default slots have default renderers, but new slots will need to implement these on their own using the `DefaultAccessoryRenderer#registerHelper` method. Such helper is just a generic helper to transform the renderer to the slots location on the entity. The only way for one to disable the Default Rendering for a given Accessory is by using the `AccessoriesRendererRegistry#registerNoRenderer` or adjusting the Data Component if actively toggled on. Furthermore the given default Render can be manipulated using the [`AccessoryRenderTransformations` data component](https://docs.wispforest.io/accessories/developer/itemstack_components#transformations) for use with Data driven Accessories. If you need simple item rendering for your Accessory, then you could look at [`SimpleAccessoryRenderer`](https://github.com/wisp-forest/accessories/blob/1.21.4/common/src/main/java/io/wispforest/accessories/api/client/SimpleAccessoryRenderer.java) . It provides a `align` method, which lets you transform the rendering to your desired location on the entity. Transformation Methods [​](https://docs.wispforest.io/accessories/developer/rendering_api#transformation-methods) ------------------------------------------------------------------------------------------------------------------ Accessories provides some helper methods to assist with transforming your `AccessoryRenderer` to various locations on the body with the primary method being `transformToModelPart` which will transform the given `PoseStack` (`MatrixStack`) to a given location on the targeted `ModelPart`. Two overloads exist for this. The first method transforms the `PoseStack` (`MatrixStack`) to the center of the `ModelPart` while the other allows for more control on the positioning on the `x`, `y`, and `z` axis by providing a number between `-1` and `1` as explained below: | Axis | Negative Values | Positive Values | | --- | --- | --- | | `x` | Left Movement | Right Movement | | `y` | Bottom Movement | Top Movement | | `z` | Back Movement | Front Movement | This method of transformation allows for the Accessories renderer to follow the model parts better allowing for compatibility with mods like Entity Model Feature and is more precises than generic transformations to where given model part may be located. Furthermore you can use `transformToFace` to target specific faces of a given model part by passing the given `Side` which such will transform the rendering to the outside of the given models face. First Person Rendering Accessories supports rendering within first person. By default anything on the humanoid entities arms is rendered this way. You can set this yourself by overriding the [`shouldRenderInFirstPerson`](https://github.com/wisp-forest/accessories/blob/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/common/src/main/java/io/wispforest/accessories/api/client/AccessoryRenderer.java#L76) method within your renderer. --- # FAQ (Frequently Asked Questions) | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/faq#VPContent) On this page FAQ (Frequently Asked Questions) [​](https://docs.wispforest.io/accessories/faq#faq-frequently-asked-questions) ================================================================================================================ General [​](https://docs.wispforest.io/accessories/faq#general) ---------------------------------------------------------------- #### Can I change what slot something is equipped to? [​](https://docs.wispforest.io/accessories/faq#can-i-change-what-slot-something-is-equipped-to) In theory you can do this, as you are able to modify an Accessory to be equippable in any slot. Due to limitations on both Minecrafts internal slot system, and Accessories API, there is no guarantee that an Accessory will _function_ if you do so. #### Is it possible to remove a slot altogether? [​](https://docs.wispforest.io/accessories/faq#is-it-possible-to-remove-a-slot-altogether) Yes, although it is not recommended for either modders or datapackers to take such drastic action, since it can really mess with mods progression or functionality. It is understandable from a modpacking perspective that you would want to do this (condensing slots, rebalancing the pack, etc.). Do note that some developers might **lock their Accessories to their own slots**, meaning that restricting/removing these could fully break those items (and by extension mods). #### Do enchantments work with equipped Accessories? [​](https://docs.wispforest.io/accessories/faq#do-enchantments-work-with-equipped-accessories) If the item is able to be enchanted with a given enchantment then it is possible for the such enchantment to work almost natively with the base equipment system found within Minecraft but some edge case may exists where the enchantment will not function properly if outside the scope of the API. ### Equipment [​](https://docs.wispforest.io/accessories/faq#equipment) #### Can I put modded items within an Accessory slot? [​](https://docs.wispforest.io/accessories/faq#can-i-put-modded-items-within-an-accessory-slot) In most cases the given mod needs to have implicit support for Accessories before the Item will function properly. We suggest that you check with mod authors if you are unsure. #### Can I put Totems within an Accessory slot? [​](https://docs.wispforest.io/accessories/faq#can-i-put-totems-within-an-accessory-slot) In the latest Releases for 1.21.2 and above, Accessories has builtin support for Totems. By default this is disabled, but you can enable the feature in the config. For developers, there exists some API for handling certain aspects of Totem logic if needed for compatibility or custom behavior. #### Can I put Gliders like Elytra within an Accessory slot? [​](https://docs.wispforest.io/accessories/faq#can-i-put-gliders-like-elytra-within-an-accessory-slot) In the latest Releases for 1.21.2 and above, Accessories has builtin support for Gliders. It is disabled by default, but you can enable the feature in the config. #### Can I put Banners in an Accessory slot? [​](https://docs.wispforest.io/accessories/faq#can-i-put-banners-in-an-accessory-slot) In the latest releases for 1.21.2 and above, Accessories has builtin support for Banners, which is enabled by default. It allows you to equip them on your head! ### Support [​](https://docs.wispforest.io/accessories/faq#support) #### Is there commands for adjusting the players slot amount? [​](https://docs.wispforest.io/accessories/faq#is-there-commands-for-adjusting-the-players-slot-amount) No. This is planned for the future, with full documentation about the commands within Accessories. #### Is there support for mods that adjust entity models and do cool animations? [​](https://docs.wispforest.io/accessories/faq#is-there-support-for-mods-that-adjust-entity-models-and-do-cool-animations) There is a desire to have mods like [Figura](https://modrinth.com/mod/figura) be supported with custom models but this requires a good deal of effort to do properly. This is planned for the future. Currently support for [Entity Model Feature](https://modrinth.com/mod/entity-model-features) is provided by some compat code, which has fixed various issues found with other Accessories APIs like Trinkets or Curios. ### Compat Layers [​](https://docs.wispforest.io/accessories/faq#compat-layers) #### I tried using the Trinkets/Curios Compat layer and its crashing, what do I do? [​](https://docs.wispforest.io/accessories/faq#i-tried-using-the-trinkets-curios-compat-layer-and-its-crashing-what-do-i-do) Make sure that both the compat layers and the Accessories version are up-to-date. This is important as the layers need to be updated frequently to adjust for any changes that may occur within Accessories. Other issues may be that a mod may be using API that isn't implemented, or accessing non-public API provided by Trinkets and Curios. It may be possible to implement/support missing API, but in some cases it might be easier if the mod itself adds direct support for Accessories. #### How do I go around adjusting slot amounts for mods using Trinkets/Curios Compat layer [​](https://docs.wispforest.io/accessories/faq#how-do-i-go-around-adjusting-slot-amounts-for-mods-using-trinkets-curios-compat-layer) For mods adding slots though Curios API it is easy. All you need to do is to adjust it through the Curious datapack method, or via the Accessories config. Trinkets can be adjust either using a datapack or through the Accessories config. You might require to adjust the format of the slot identifier to something like `trinket_group_{slot_group_name_here}-{slot_name_here}` in order for it to work properly. This is due to Trinkets API using groups as logical paths, meaning the name of the slot within Accessories requires the group name to be combined with the slot name to prevent issues. Developer [​](https://docs.wispforest.io/accessories/faq#developer) -------------------------------------------------------------------- #### Differences between Accessories and other Accessory API's [​](https://docs.wispforest.io/accessories/faq#differences-between-accessories-and-other-accessory-api-s) Some notable differences compared to the likes of Trinkets and Curios: ##### Mod Development [​](https://docs.wispforest.io/accessories/faq#mod-development) * Full Multiloader support for both major platforms (**Fabric** and **Neoforge**) * Fully programmatic method for creating unique slots * Support for Nested Accessories (Accessories within Accessories) * Expansive support for ModelPart targeting, with support for EMF ##### Gameplay [​](https://docs.wispforest.io/accessories/faq#gameplay) * Multiple different screen variants depending on the desired style of play * Full support for Data Driven Accessories * Supports Cosmetic Armor/Accessories out-of-the-box * Additional toggleable features for equipping Banners as Accessories (on your head!) * Additional toggleable features for equipping Totems or Gliders as Accessories ##### Performance [​](https://docs.wispforest.io/accessories/faq#performance) * Implement caching to alleviate bottle necks with querying Accessory inventories #### Reason behind using owo-lib in the development of Accessory [​](https://docs.wispforest.io/accessories/faq#reason-behind-using-owo-lib-in-the-development-of-accessory) The main reason behind using owo-lib is down to its useful API's that leads to the development of Accessories being unhindered when it comes to UI, serialization, networking, and configuration. The library is used to its full extent, apart from some minor core features. I decided to use it to keep development time down and allow for quicker implementation of features instead of developing and maintaining all that API from scratch. All of this is done to create a better experience for players. --- # ItemStack Data Components | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/developer/itemstack_components#VPContent) On this page ItemStack Data Components [​](https://docs.wispforest.io/accessories/developer/itemstack_components#itemstack-data-components) =============================================================================================================================== General [​](https://docs.wispforest.io/accessories/developer/itemstack_components#general) ------------------------------------------------------------------------------------------- Below is the current set of available Data Components for ItemStacks that are supported with documentation about their function and format. ### Attribute Modifiers [​](https://docs.wispforest.io/accessories/developer/itemstack_components#attribute-modifiers) Any ItemStack can have Attributes added onto it with the `accessories:attributes` Item Component. It is composed from a list of attribute entries similar to Minecraft's default implementation. Below is an example: JSON { "accessories:attributes": { "modifiers": [\ {\ "type": "minecraft:attack_damage",\ "id": "example:increased_damage_boost",\ "amount": 1,\ "operation": "add_value",\ "slot_name": "hat",\ "is_stackable": false,\ }\ ] } } | Field | Usage | | --- | --- | | `"type"` | Refers to the attribute type that should be adjusted with the given modifier data. This can either be a slot attribute or a registered slot. | | `"id"` | The unique resource location to identify this modifier | | `"amount"` | The amount of change from the modifier | | `"operation"` | The type of calculation operation that should be performed out of the valid values. | | `"slot_name"` | Specify a slot to add a strict requirement to where the accessory must be equipped for the effect to apply. Use `"any"` to allow for the attribute to be applied to whatever slot the given item can be equipped into. | | `"is_stackable"` | Allows for additional copies of the attribute to function together as a compounding effect | Stacking Attributes If the given attribute entry is desired to be stacked with other equipped accessory it may be good to use `"is_stackable"` which indicates that the given `"id"` passed should be appended with slot information to allow. ### Slot Validation [​](https://docs.wispforest.io/accessories/developer/itemstack_components#slot-validation) Any ItemStack can define invalid or valid slots to which it can be equipped to using the `accessories:slot_validation` Item Component. Using it allows you to add to or override the equipability for an existing Accessory. JSON { "accessories:slot_validation": { "valid_slot": [\ "hat"\ ] "invalid_slot": [\ "shoes"\ ] } } | Field | Usage | | --- | --- | | `"valid_slots"` | An array of string values of valid slots that the stack can be equipped into | | `"invalid_slots"` | An array of string values of invalid slots that the stack **can not** be equipped into | ### Stack Size [​](https://docs.wispforest.io/accessories/developer/itemstack_components#stack-size) This Data Component allows you to adjust the max stack size of an Item for a single Accessory Slot. This can be useful in cases where you want to only allow equipping a specific amount of items int it (E.G. heart containers, or consumables). Note that your size override must be **smaller** than the max stack size of the Item. JSON { "accessories:stack_size": { "use_stack_size": false, "size_override": 69 } } | Field | Usage | | --- | --- | | `"use_stack_size"` | Use the stack's given max size as the amount allowed to stack to within Accessories | | `"size_override"` | The amount that the size should be overridden to instead of stack size | ### Accessory Nest [​](https://docs.wispforest.io/accessories/developer/itemstack_components#accessory-nest) Accessories has a system that allows for nesting an Accessory within another Accessory. This can only be done explicitly with the API (code), or when creating a new ItemStack that has the `nested_accessories` Data Component. Example: JSON { "accessories:nested_accessories": { "accessories": [\ {\ "id": "minecraft:diamond",\ "count": 1,\ "components": {\ "accessories:attributes": {\ "modifiers": [\ {\ "type": "minecraft:attack_damage",\ "id": "example:increased_damage_boost",\ "amount": 1,\ "operation": "add_value",\ "slot_name": "any",\ "is_stackable": false,\ }\ ]\ }\ }\ }\ {\ "id": "minecraft:emerald",\ "count": 1,\ "components": {\ "accessories:attributes": {\ "modifiers": [\ {\ "type": "minecraft:attack_speed",\ "id": "example:increased_damage_speed",\ "amount": 1,\ "operation": "add_value",\ "slot_name": "any",\ "is_stackable": false,\ }\ ]\ }\ }\ }\ ] } } | Field | Usage | | --- | --- | | `"accessories"` | A list of `ItemStack`s that have various components and will be used to get Accessory info from when equipping the nest | Nest Equipability Make sure to remember the equipability of your nest. If your Nested Accessory needs to change its equipability dynamically, you might want to add the Slot Validation component to the nest. Alternatively, just using a tag or Tag Predicate is fine. Rendering [​](https://docs.wispforest.io/accessories/developer/itemstack_components#rendering) ----------------------------------------------------------------------------------------------- This section covers the various components you can use to adjust certain rendering aspects of Accessories. ### Rendering Override [​](https://docs.wispforest.io/accessories/developer/itemstack_components#rendering-override) Any equippable ItemStack (or Accessory) can have a component called `accessories:render_override`, which allows you to force enable/disable the default renderer or the armor renderer. JSON { "accessories:render_override": { "default_render_override": false, "armor_render_override": true } } | Field | Usage | | --- | --- | | `"default_render_override"` | An optional boolean value indicating if the default render should be used | | `"armor_render_override"` | An optional boolean value indicating if the armor render should be used | ### Rendering Transformations [​](https://docs.wispforest.io/accessories/developer/itemstack_components#rendering-transformations) For any Accessory that uses the default renderer: It is possible to use the `render_transformations` data component to adjust the positioning, rotation, and scale of the rendering. Some examples are wearing a block as a hat, or placing an item on your arm. JSON { "accessories:render_transformations": { "disable_default_translations": true, "transformations": [\ {\ "type": "translation",\ "value": [-25, 0, 5]\ },\ {\ "type": "axis_rotation",\ "value": {\ "angle": 90,\ "axis": [1, 0, 1],\ }\ }\ ] } } #### Transformations [​](https://docs.wispforest.io/accessories/developer/itemstack_components#transformations) TranslationRaw RotationAxis RotationScaleTransform ToMatrix Translates the given rendering position based on the provided offset `Vector`. JSON { { "type": "translation", "value": [-25, 0, 5] } } The `"value"` should be a vector with the offset being in the `x`, `y`, and `z` format. --- # Adjusting Accessory Equipablity | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/binding_accessories_to_slots#VPContent) Return to top Adjusting Accessory Equipablity [​](https://docs.wispforest.io/accessories/general/binding_accessories_to_slots#adjusting-accessory-equipablity) ================================================================================================================================================= To control general equipability of items into slots it is required to make Slot Predicates. Accessories has three builtin predicates that can be used by both Modders and Datapackers alike with such being Item Tags, Data Component, and Attribute. Modders can add custom predicates programmatically to allow for more control over what can be equipped if needed for new slots or existing slots. For such you can register a custom predicate using the `SlotPredicateRegistry#register` method for your custom `SlotBasedPredicate`. Altering Existing Predicates It is best to not restrict what can be equipped within the **Default** Slots or others due to the possibility of other Modders or Datapackers accessories from being able to be equipped with the given slots. Adding new predicates that allow for new items to be equipped should attempt to return `Tristate.DEFAULT` value when possible. * `accessories:tag` By placing items within the slots corresponding Item [Tag](https://minecraft.wiki/w/Tag) , you can allow for the given item to be equipped within the target slot. For example you if you want to add an item to the default `hat` slot, you need to make a Datapack and add the given item to the `accessories:hat` tag located within the folder structure like so: `data/accessories/tags/item/hat.json` * `accessories:component` By adding the `"accessories:slot_validation"` [Data Component](https://minecraft.wiki/w/Data_component_format) to a given ItemStack you can adjust what slots a given item is valid for by adjusting the components `"valid_slots"` and `"invalid_slots"` as instructed within the info about the given [component](https://docs.wispforest.io/accessories/developer/itemstack_components#slot-validation) * `accessories:attribute` It is **recommend** to use `"accessories:component"` instead of this for better control but by adding attributes linked to specific slots you can control what a given accessory item can be equipped within. Such can either be done within `"accessories:attributes"` Data Component or programmatically with the `AccessoryAttributeBuilder`. --- # Slot Adjustments Methods | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#VPContent) On this page Slot Adjustments Methods [​](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#slot-adjustments-methods) ============================================================================================================================ Slots were designed with the potential for the given slot amount to be changeable. This can be accomplished either by using the data format through the various `operation` type's, or by using the other methods provided. Attribute Modifier [​](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#attribute-modifier) ---------------------------------------------------------------------------------------------------------------- Accessories adds a system for adjusting a specific Slot types amount of slots given to the entity who equips the Accessory. Such can be done either though a programmatic or the Data Component way. ### Programmatic Method [​](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#programmatic-method) Accessories provides a programmatic way of adjusting slot amounts, which is similar in design to the data pack method. The method calls for this are found either on the `AccessoriesCapability` or the targeted `AccessoriesContainer` itself. The following example shows how to add and remove additional ring slots, using the capability within a test Accessory: java public class RingIncreaser implements Accessory { private static final ResourceLocation RING_ATTRIBUTE_LOCATION = ResourceLocation.fromNamespaceAndPath("test", "additional_rings") //... @Override public void onEquip(ItemStack stack, SlotReference reference) { var map = HashMultimap.create(); map.put("ring", new AttributeModifier(RING_ATTRIBUTE_LOCATION, 100, AttributeModifier.Operation.ADDITION)); reference.capability().addTransientSlotModifiers(map); } @Override public void onUnequip(ItemStack stack, SlotReference reference) { var map = HashMultimap.create(); map.put("ring", new AttributeModifier(RING_ATTRIBUTE_LOCATION, 100, AttributeModifier.Operation.ADDITION)); reference.capability().removeSlotModifiers(map); } //... } Another Programmatic method of such is by adjusting the slots Dynamic Modifiers instead of adding to entity on equipping of the accessory: java public class HatIncreaser implements Accessory { private static final ResourceLocation HAT_ATTRIBUTE_LOCATION = ResourceLocation.fromNamespaceAndPath("test", "additional_hats") //... @Override public void getDynamicModifiers(ItemStack stack, SlotReference reference, AccessoryAttributeBuilder builder){ // Exclusive Refers to only one instance of the modifier is allowed builder.addExclusive(SlotAttribute.getSlotAttribute("hat"), HAT_ATTRIBUTE_LOCATION, 4, AttributeModifier.Operation.ADDITION); // Stackable Refers to any amount of the given modifier is allowed (Preappends Slot Info like the type and index to the location) builder.addStackable(SlotAttribute.getSlotAttribute("hat"), HAT_ATTRIBUTE_LOCATION, 4, AttributeModifier.Operation.ADDITION); } //... } ### Data Component [​](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#data-component) As outlined within the [`accessories:attributes`](https://docs.wispforest.io/accessories/developer/itemstack_components#attribute-modifiers) Item Component you can add such to a given ItemStack to adjust the amount of equippable slots _when the accessory is equipped_. Config File [​](https://docs.wispforest.io/accessories/general/adjusting_slot_amount#config-file) -------------------------------------------------------------------------------------------------- The Accessories config file allows you to adjust the slot amount globally. Keep in mind that changing the defaults can heavily change the balance of your game/modpack. You can go well above a hundred ring slots for example, although that would easily clutter up your Accessory screen. The config file is located within the `./config/` folder within your Minecraft instance under the file `accessories`. You can then specify new slot amounts using the `"modifiers`" field in the given format: json { //... "modifiers": [ // Array of json objects comprising the below structure\ {\ "slotType": "test", // Targeted Slot Name\ "amount": 23 // The Amount to shoot for as the base\ }\ ] } --- # Default Slots | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/defaulted_slots#VPContent) On this page Default Slots [​](https://docs.wispforest.io/accessories/general/defaulted_slots#default-slots) ================================================================================================ By default Accessories provides builtin slots for use by other modders to give a starting point of shared slots. This is done to prevent a flood of slots with alternatives of the same concept, which leads to a cluttered UI and confusing equipment management. We recommend you to try and use the available slots starting out unless your Accessory item does not fit under the default slots. For that, we suggest you consider [making your own through data](https://docs.wispforest.io/accessories/general/slot_types#data-pack-format) or by using the [Unique Slot API](https://docs.wispforest.io/accessories/general/slot_types#unique-slot-api) . Default Implementations [​](https://docs.wispforest.io/accessories/general/defaulted_slots#default-implementations) -------------------------------------------------------------------------------------------------------------------- Below is the current default implemented list of Slot Types provided: | Name | Usage | Examples | | --- | --- | --- | | `charm` | Items equipped into a pocketed area of the entity's body | Magic Stuff | | `hat` | Items equipped on top of the entity's head | Crowns or Hats | | `face` | Items equipped over the entity's face i.e. a mask | Masks or Glasses | | `cape` | Items equipped around the entity's neck and drape down their back | Cloaks or Shirts | | `necklace` | Items equipped around the entity's neck and hang in the front of their body | Necklaces or Collar | | `back` | Items equipped on the entity's back around the chest or around the arms on both side | Backpacks or Shields | | `hand` | Items equipped on the entity's hands either covering them fully or partially | Gloves or Gauntlets | | `ring` | Items equipped on the entity's fingers | Rings | | `wrist` | Items equipped around the entity's wrist | Watches or Bracelets | | `belt` | Items equipped around the entity's waist | Belt or Pouches | | `anklet` | Items equipped around the entity's ankle | Anklets | | `shoes` | Items equipped on the entity's feet | Shoes or Socks | --- # Binding Slots to Entities | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/binding_slots_to_entities#VPContent) On this page Binding Slots to Entities [​](https://docs.wispforest.io/accessories/general/binding_slots_to_entities#binding-slots-to-entities) ================================================================================================================================== It is required that each Slot created needs to be implicitly bound to the desired entity type. By default, the given implemented slots are bound to any entity type found within the `accessories:defaulted_targets` **EntityType** tag which currently includes the `Player` and `ArmorStand` type. Data Pack Format [​](https://docs.wispforest.io/accessories/general/binding_slots_to_entities#data-pack-format) ---------------------------------------------------------------------------------------------------------------- To bind a slot to a given **EntityType** you will need to create a `.json` file at the following location: `data/{replace_with_pack_namespace}/accessories/entity/`. This file will need to contain to required fields: `entities` and `slots`. | Field Keys | Data Type | Description | | --- | --- | --- | | `"entities"` | String\[\] | A list containing either specific resource locations of Entity Types or an Entity Type Tag (prefixed with a hashtag `#`) | | `"slots"` | String\[\] | A list of all slot names to be available to the given entities | The example below is what Accessories uses to bind the Builtin Slots to the defaulted entity targets: json { "replace": false, "entities": [\ "#accessories:defaulted_targets"\ ], "slots": [\ "hat",\ "face",\ "necklace",\ "cape",\ "back",\ "hand",\ "ring",\ "wrist",\ "belt",\ "anklet",\ "shoes",\ "charm"\ ] } --- # Creating Slot Groups | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/slot_groups#VPContent) On this page Creating Slot Groups [​](https://docs.wispforest.io/accessories/general/slot_groups#creating-slot-groups) ========================================================================================================== Slot Groups are mainly a cosmetic system meant to wrap certain slots in specific parts the body as a way to better order specific sets of slots and act as an easy method to quickly move to such specific slots within the Accessories Screen. Data Pack Format [​](https://docs.wispforest.io/accessories/general/slot_groups#data-pack-format) -------------------------------------------------------------------------------------------------- To create your own Slot group or adjust an existing group, you will need to need to make a new `.json` file at the following location, `data/{replace_with_pack_namespace}/accessories/group/`, **that is named as the groups name**. Below is a detailed example on the given format: json { "replace": false, "order": 120, "slots": [\ "charm"\ ], "icon": "accessories:gui/group/any" } | Field Keys | Data Type | Description | | --- | --- | --- | | `"order"` | Integer | The order number to which the group will appear within the Accessories Screen with lower being placed first and higher numbers being placed last | | `"slots"` | String\[\] | The slot names to which belong to the given Slot Group | | `"icon"` | String | The resource location of the given slot icon in which is used to find the texture within the block atlas | --- # Creating and Modifying Slots | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/general/slot_types#VPContent) On this page Creating and Modifying Slots [​](https://docs.wispforest.io/accessories/general/slot_types#creating-and-modifying-slots) ========================================================================================================================= If the information below is still leaving you with questions, you can look at each platform's test mod \[[Fabric](https://github.com/wisp-forest/accessories/tree/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/fabric/src/testmod)\ , [Neoforge](https://github.com/wisp-forest/accessories/tree/fa06f044f5c7486b26a8c0774f7ca3edbd256cad/neoforge/src/testmod)\ \] for starting points. You can also ask questions within the [Discord](https://discord.gg/xrwHKktV2d) for more assistance. Creating a slot within Accessories requires you follow the two options available to you: * Datapack Method: Allows for full ability to tweak Slot Properties and its bound Entities within `json` data files loaded on server start * Programmatic Method: More restrictive control over a given slot type via the Unique Slot API Notice for Datapacks When using the Datapack method its recommended that you design your mod with the ability to be adjusted by end users/other datapackers. This is because of Accessories base systems, which allow for items to be moved from one slot to another, removed from your slot, or even preventing an item from being equipped in any slot. It may be your intention to provide some sense of balance, but this will lead to issues for Modpack makers, who usually want to balance around other mods. Data Pack Format [​](https://docs.wispforest.io/accessories/general/slot_types#data-pack-format) ------------------------------------------------------------------------------------------------- To create your own slot type or adjust an existing slot, you will need to need to make a new `.json` file at the following location, `data/{replace_with_pack_namespace}/accessories/slot/`, **that is named as the slots name**. **For Unique slots**: it is required that you have the namespace of the given slot within the folder structure as follows: `data/{replace_with_pack_namespace}/accessories/slot/{unqiue_slot_namespace_here}/`. Below is an example of the `back` slot file located within Accessories [here](https://github.com/wisp-forest/accessories/blob/c41504c63f5c608e1e0ea249fae8b1a152c92f29/common/src/main/resources/data/accessories/accessories/slot/back.json) : json { "replace": false, "amount": 1, "operation": "set", "order": 1000, "icon": "accessories:gui/slot/back" } | Field Keys | Data Type | Description | | --- | --- | --- | | `"order"` | Integer | The number which decides in what order the slot will appear inside the Accessories Screen. Lower numbers are placed first and higher numbers are placed last | | `"amount"` | Integer | Used when calculating the base size of the given slot using the given operation's: `"set"`, `"add"`, or `"remove"` | | `"operation"` | String | The specific operation used in combination with the specified `amount` | | `"validators"` | String\[\] | The ResourceLocation's (`Identifier`) of all predicates to be used for the given slot | | `"drop_rule"` | String | The specific rule used when attempting to drop the Accessories: `"default"`, `"keep"`, `"drop"`, `"destroy"` | | `"icon"` | String | The resource location of the given slot icon in which is used to find the texture within the block atlas | Icon Location It is recommended that the location for the icon follows the `assets/{replace_with_pack_namespace}/gui/slot/` convention. This is because any textures here are put within the block atlas (to allow for ticking). If this is not followed, it is up to the developer to add the texture to the block atlas for rendering to work To modify existing slots there are multiple methods to such: * Modify Amount: Create a file with the same **same path** and **same name** with adjustments to amount either though the `add` or `remove` operation type * Replace File: Create a file with the **same namespace**, **same path**, and **same name** that you then adjust with the `replace` field set to `true` which will replace * Replace Data (Requires Datapack Ordering): Move your datapack using some tool or method(Not included by default) that allows for the adjustment of ordering allowing for your slot file to apply after the inital one allowing you to modify aspects of such Unique Slot API [​](https://docs.wispforest.io/accessories/general/slot_types#unique-slot-api) ----------------------------------------------------------------------------------------------- This API is the alternative method for creating a slot which comes with the benefits of being able to lock down various aspects of a slot like equipability, which Entity a slot is bound to, and prevents certain operations like resizing from occurring. Note that unique slots will not show up in the main Accessory Screen. Wit this you get the freedom to implement your own custom screen/system using Accessories as your backend/API target to interact with inventory. A breakdown of how such is to implement is below: java public class UniqueSlotExample implements UniqueSlotHandling.RegistrationCallback { private static final ResourceLocation MAGIC_BALL_PREDICATE = ResourceLocation.fromNamespaceAndPath("example", "magic_ball"); public static final UniqueSlotExample INSTANCE = new UniqueSlotExample(); private UniqueSlotExample(){ SlotPredicateRegistry.registerPredicate(slotPredicate1, SlotBasedPredicate.ofItem(item -> item.equals(ExampleItems.MAGIC_BALL))); } private static SlotTypeReference MAGIC_BALL_REFERENCE; @Override public void registerSlots(UniqueSlotHandling.UniqueSlotBuilderFactory factory) { MAGIC_BALL_REFERENCE = factory.create(ResourceLocation.fromNamespaceAndPath("example", "magic_ball"), 1) .slotPredicates(MAGIC_BALL_PREDICATE) .validTypes(EntityType.PLAYER) .build(); } @Nullable public static SlotTypeReference magicBallRef() { return MAGIC_BALL_REFERENCE; } // Call init method within your mods initializer to add the class to the main Unique Slot Handling event public static void init() { UniqueSlotHandling.EVENT.register(UniqueSlotTest.INSTANCE); } } Slot Initialization You must call the init function of the example class or register the event invoker yourself The given factory object that is returned by the registered method invocation or implementing object allows you to get a `UniqueSlotBuilder` which has the following methods: | Field Keys | Description | | --- | --- | | `slotPredicates(ResourceLocation... locations)` | Used to pass the desired Slot Predicates for this slot as none are set by default | | `validTypes(EntityType... types)` | Used to pass any entity types that should be bound with this slot | | `strictMode(boolean value)` | Used to prevent any modifications to key data like the given slot predicates or what entity have this slot bound | | `allowResizing(boolean value)` | Used to prevent adjustments to the slot amount either though datapack or attributes | | `allowEquipFromUse(boolean value)` | Used to prevent any Accessory valid for this slot type from being quickly equipped from hand | | `allowTooltipInfo(boolean value)` | Used to prevent any tooltip information about this slot from appearing within a given Accessories Tooltip | --- # Wisp Forest Docs [Skip to content](https://docs.wispforest.io/#VPContent) Wisp Forest Docs ================ Here at Wisp Forest© we employ Wisp Tech Support™ magic, which solves your problem when you ask [Wisp Forest GitHub](https://github.com/wisp-forest) [Legacy Documentation](https://docs.wispforest.io/legacy/) ![](https://docs.wispforest.io/wf-header.png) [![](https://docs.wispforest.io/owo-icon.png)\ \ oωo-lib\ -------\ \ A general utility, GUI and config library for modding on Fabric and Quilt\ \ Documentation](https://docs.wispforest.io/owo/setup) [![](https://docs.wispforest.io/lavender-icon.png)\ \ Lavender\ --------\ \ A modern Guidebook API and alternative to Patchouli\ \ Documentation](https://docs.wispforest.io/lavender/setup) [![](https://docs.wispforest.io/braid-icon.png)\ \ braid-ui\ --------\ \ An modern, lightweight, experimental UI framework for desktop and Minecraft\ \ Documentation](https://docs.wispforest.io/braid/home) [![](https://docs.wispforest.io/accessories-icon.png)\ \ Accessories\ -----------\ \ An extendable and data-driven Accessory Mod for Minecraft\ \ Documentation](https://docs.wispforest.io/accessories/home) [![](https://docs.wispforest.io/isometric-renders-icon.png)\ \ Isometric Renders\ -----------------\ \ Generates clean, adjustable isometric screenshots of game objects directly in-game\ \ Documentation](https://docs.wispforest.io/isometric-renders/home) [![](https://docs.wispforest.io/numismatic-overhaul-icon.png)\ \ Numismatic Overhaul\ -------------------\ \ Terraria-style currency in Minecraft\ \ Documentation](https://docs.wispforest.io/numismatic-overhaul/home) [![](https://docs.wispforest.io/alloy-forgery-icon.png)\ \ Alloy Forgery\ -------------\ \ Forge some alloys\ \ Documentation](https://docs.wispforest.io/alloy-forgery/home) --- # Accessories | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/accessories/home#VPContent) On this page Accessories [​](https://docs.wispforest.io/accessories/home#accessories) ========================================================================= Welcome to the Accessories Wiki! This wiki attempts to how to use the API within Accessories, and gives examples to help you learn how to implement your own Accessories. These docs were written for the 1.2.0 version of Accessories and above, so if you are using any **older** versions there might be missing features or methods might have different names. Work-in-progress The Accessories documentation is still being improved, and currently lacks a quick start guide. If you are unsure about something, try to reference the JavaDoc or simply come ask in [our Discord](https://discord.gg/xrwHKktV2d) - we'll be happy to help Overview [​](https://docs.wispforest.io/accessories/home#overview) ------------------------------------------------------------------- Accessories is a data-driven accessory mod for NeoForge and Fabric and is designed to be loader agnostic. This allows for more common code base than typically implementations. The API is based on the works of [Curios](https://github.com/TheIllusiveC4/Curios) and [Trinkets](https://github.com/emilyploszaj/trinkets) , ### Credit [​](https://docs.wispforest.io/accessories/home#credit) Thank you both [TheIllusiveC4](https://github.com/TheIllusiveC4) and [emilyploszaj](https://github.com/emilyploszaj) for their work on accessory mods for Minecraft. Combined with assistance of [Noaaan](https://github.com/Noaaan) , [enjarai](https://github.com/enjarai) , and [bconlon](https://github.com/bconlon1) for reviewing this documentation. #### Why Accessories? [​](https://docs.wispforest.io/accessories/home#why-accessories) * More powerful and better API * Better support for rendering accessories * Supports multiloader setups, which makes porting and maintenance much easier * Built-in vanity slots, which allows for more customization Features [​](https://docs.wispforest.io/accessories/home#features) ------------------------------------------------------------------- * Multiloader design allowing for both Platform Specific mods and support for Multiloader mods using Common code area * Full support for `LivingEntity`, meaning the API supports not only players, but other mobs like Zombies, Skeletons or even Wolves! * Integration with DataComponents, which allows any ItemStack to be usable as an accessory (useful for testing or just messing around). * Datapackable/Programmatic Unique Slot API allowing for either more user adjustable design or more restrictive method for mod specific accessory handling * Events for adjusting behavior of Minecraft's mechanics depending on Accessories, like disabling Enderman aggro or Piglin neutrality. * Optional compatibility layers for both competing Accessory APIs, which allows any accessory to be usable regardless of modpack configuration. * Designed with Sinytra Connector in mind! * Fully configurable support for gliders (I.E. Elytra), Totems, and Banners out of the box! --- # Recipe Adaptation | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/alloy-forgery/recipe-adaptation#VPContent) On this page Recipe Adaptation [​](https://docs.wispforest.io/alloy-forgery/recipe-adaptation#recipe-adaptation) ==================================================================================================== Since 2.1.0, Alloy Forgery added a system to adapt existing recipes to alloy forging recipes. Currently, only Blast Furnace recipes are adapted to work with the forge. Developed alongside this adaptation system is the ability to create tags for Recipes with such following the same format as any other tag just being the tags should be placed within the `{your_namespace_here}/tags/recipes/` folder within your datapack. Blast Furnace Adaptation [​](https://docs.wispforest.io/alloy-forgery/recipe-adaptation#blast-furnace-adaptation) ------------------------------------------------------------------------------------------------------------------ By default, Alloy Forgery attempts to adapt all Blast Furnace recipes to work within the Alloy Forge combined with giving an output increase when reaching tier 3 forge. This System can be controlled by two methods: ### Tags [​](https://docs.wispforest.io/alloy-forgery/recipe-adaptation#tags) Included are some tags that are used to adjust the behavior of the recipe adapter. One of these is a blacklist for recipes altogether (`alloy_forge:blacklisted_blasting_recipes`) which, for each recipe ID within it, prevents the corresponding recipe from being adapted into an Alloy Forge recipe. You can also specifically blacklist a blasting recipe from gaining the increased output by putting it within the (`alloy_forge:blacklisted_increased_blasting_outputs`) recipe tag. ### Config [​](https://docs.wispforest.io/alloy-forgery/recipe-adaptation#config) More recent versions of Alloy Forgery include a config file which contains some more options on controlling blasting adaption, such outlined below: | Option | Description | | --- | --- | | `allowHigherTierOutput` | Allows for the ability to toggle on or off the tier increase on **ALL** Blasting Recipes. | | `allowBlastingFurnaceAdaption` | Allows for the ability to toggle on or off the blasting adaption for **ALL** Blasting Recipes. | | `baseInputAmount` | Adjusts the required input amount for the adapted Blasting Recipe. Such functions as a multiplier for both the input and output meaning that with the default value of '2', the required amount needed to craft is '2' with an output of '2'. | | `higherTierOutputIncrease` | Controls the amount of increased output that is gotten when using a tier 3 Alloy Forge if such tier output was not disabled. | --- # Comparison to owo-ui | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/braid/comparison-to-owo-ui#VPContent) Return to top Comparison to owo-ui [​](https://docs.wispforest.io/braid/comparison-to-owo-ui#comparison-to-owo-ui) ===================================================================================================== braid's design fundamentally differs from that of owo-ui. Whereas owo-ui is a traditional retained-mode framework, braid is best described as a reactive one. To illustrate what this difference means, consider the following code snippets, both roughly containing the necessary code to implement a basic counter: owo-uibraid java public Component createCounter(int startFrom) { var count = new MutableInt(startFrom); var counterLabel = Components.label(Text.literal(String.valueOf(count.intValue()))); var counterButton = Components.button(Text.literal("count"), button -> { count.add(1); counterLabel.setText(Text.literal(String.valueOf(count.intValue()))); }); return Containers.verticalFlow(Sizing.content(), Sizing.content()) .child(counterLabel) .child(counterButton) .horizontalAlignment(HorizontalAlignment.CENTER) .verticalAlignment(VerticalAlignment.CENTER); } java public class Counter extends StatefulWidget { public final int startFrom; public Counter(int startFrom) { this.startFrom = startFrom; } public WidgetState createState() { return new State(); } public static class State extends WidgetState { public int count = 0; @Override public void init() { this.count = this.widget().startFrom; } @Override public Widget build(BuildContext context) { return new Center( new Column( Label.literal(String.valueOf(this.count)), new MessageButton( Text.literal("count"), () -> this.setState(() -> this.count++) ) ) ); } } } Besides the obvious fact that the braid version is less terse and thus takes up more visual space, this immediately highlights a two very important differences: * There is no functional distinction between widgets provided by the library (like `Center` or `MessageButton`) and new widgets authored by the user (like `Counter`). This means that there is clear way to package reusable components of a UI, unlike owo-ui where this concept of a "component made up of other components" doesn't really exist * For owo-ui, it being a retained-mode framework, the user is responsible for managing the lifecycle of their components and keeping the state of the UI in sync with the state of the data it represents (by manually tracking `count` and applying it to the label when necessary). For braid, because all dependencies of the `build` method are encapsulated clearly in the surrounding `WidgetState`, the library can simply re-run the build method when a state change happens (caused by the call to `setState`). This encodes the current data (the state's fields) into a format the library understands (a widget tree), which it can then apply to the underlying widget instances to automatically synchronize the UI. This is what it means for braid to be _reactive_ - it automatically _reacts_ to state changes by updating the UI appropriately without the user needing to figure out the exact things that need to happen to transition the old state to the new state, they simply describe what the data looks like at the current moment. In general, the API of braid is centrally structured around **describing state changes**. This is diametrically opposed to owo-ui, where manual management of the internal UI data structures is front and center. This mandates a shift in thinking when developing with either library, but we have found that it is generally much more natural to think about state rather than UI internals and that it leads to more productive development overall. --- # Wisp Forest Docs [Skip to content](https://docs.wispforest.io/braid/home#VPContent) Return to top ![the braid logo](https://docs.wispforest.io/assets/icon.DHc5L7fe.svg) braid is a modern, reactive UI framework for Desktop and Minecraft, written in [Dart](https://dart.dev/) and Java respectively. Its core concepts are strongly inspired by libraries like [React](https://react.dev/) and especially [Flutter](https://flutter.dev/) . The development of braid was prompted by the desire to address a number of important shortcomings in owo-ui (owo-lib's [legacy UI framework](https://docs.wispforest.io/owo/ui/) ). Over time, it has grown into a fully-fledged and flexible but also convenient library built on modern principles. Alpha Cycle **braid** is currently in early development, highly experimental and has no stable API. **braid for Minecraft** is in its alpha cycle and does not guarantee a stable API either. While we will generally try our best not to break existing code, we won't be afraid to make breaking changes which we deem necessary for the project's long-term health. While the majority of concepts and API elements transfer 1:1 between the standalone reference implementation and owo-lib's Minecraft-specific version, there are a number of notable differences to be aware of. This documentation will primarily be written for the Minecraft implementation as we expect that to be our primary user base, but we'll attempt to provide relevant explainers and documentation in places where they deviate. --- # Json5 Data Loading | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/data-extensions/json5#VPContent) On this page Json5 Data Loading [​](https://docs.wispforest.io/owo/data-extensions/json5#json5-data-loading) ================================================================================================ oωo lets you use [JSON5](https://json5.org/) files anywhere a json file is expected. This means you can use comments, trailing commas, single quotes, and [more](https://spec.json5.org/) . Enabling JSON5 [​](https://docs.wispforest.io/owo/data-extensions/json5#enabling-json5) ---------------------------------------------------------------------------------------- Before you can use Json5 files, you need to enable the feature by putting a file named `owo-json5` in the root of any resource/data pack you want to use Json5 in. The contents of the file do not matter, it just needs to exist. NOTE Json5 lang files enable all language extensions by default. See [Rich Translations](https://docs.wispforest.io/owo/data-extensions/rich-translations) and [Nested Lang](https://docs.wispforest.io/owo/data-extensions/nested-lang) for details. --- # Using the option system for fun and profit | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/config/options#VPContent) On this page Using the option system for fun and profit [​](https://docs.wispforest.io/owo/config/options#using-the-option-system-for-fun-and-profit) ========================================================================================================================================= How and why [​](https://docs.wispforest.io/owo/config/options#how-and-why) --------------------------------------------------------------------------- owo-config represents each option in your config as an instance of the `Option` class. You can obtain this internally used instance via the `optionByKey(...)` method on your wrapper by passing in the option's key. The returned object exposes a number of useful methods and properties of the option it represents. In general, the options are a convenient way to handle the individual settings in a config programmatically. For actual usage in code it is usually preferable to use the methods generated on your wrapper, which internally delegate to their underlying options. Commonly interesting methods [​](https://docs.wispforest.io/owo/config/options#commonly-interesting-methods) ------------------------------------------------------------------------------------------------------------- ### `backingField()` [​](https://docs.wispforest.io/owo/config/options#backingfield) Returns the field which is internally used to serialize the option's value and for storing the actual value when the `Option` object is detached. You can use it for annotation lookups as well as general reflection, if by getting the `field` record component. Technically, this also allows bypassing constraint and detached state, however any changes made won't actually ever apply. ### `constraint()` [​](https://docs.wispforest.io/owo/config/options#constraint) Returns the constraint placed on this option's value, or `null` if the option is unconstrained. The returned object contains both a formatted description of the constraint and the actual predicate which you can use for verifying values. ### `observe(...)` [​](https://docs.wispforest.io/owo/config/options#observe) Registers a change callback which is invoked every time the value of this option actually _changes_ - this means it won't be invoked if `set(...)` is called with a value for which the current value's `equals(...)` method returns `true`. --- # Nested Lang | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/data-extensions/nested-lang#VPContent) On this page Nested Lang [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#nested-lang) ======================================================================================== Nested Lang is a simple DRY (Don't Repeat Yourself) system for language files. It allows you to nest keys inside of objects and arrays to reduce repetition and make your language files cleaner, easier to read and smaller on disk. IMPORTANT Nested Lang is flattened prior to language loading, this ensures full compatibility with any other language modifications and no performance impact. In fact, nesting may even provide _slight_ performance improvements by decreasing the size of the language file on disk. Setup [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#setup) ---------------------------------------------------------------------------- Before you can use nested lang, you need to enable it in 1 of 2 ways: 1. Enable oωo's [Json5 Data Loading](https://docs.wispforest.io/owo/data-extensions/json5) and use a `.json5` file. 2. Add a key `owo:nested_lang` or `owo:extended_lang` with the value `true` or `1` anywhere in your lang file (at the top level). Object Nesting [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#object-nesting) ---------------------------------------------------------------------------------------------- When writing language files, you tend to have a lot of repeated text. For example, a mod containing a lot of items might contain this in its language file: en\_us.json json { "item.modid.firstItem": "First Item", "item.modid.secondItem": "Second Item", "item.modid.thirdItem": "Third Item", "item.modid.fourthItem": "Fourth Item", "item.modid.fifthItem": "Fifth Item", "item.modid.sixthItem": "Sixth Item", "item.modid.seventhItem": "Seventh Item", "item.modid.eighthItem": "Eighth Item", "item.modid.ninthItem": "Ninth Item", "item.modid.tenthItem": "Tenth Item" } That alone has 10 instances of `item.modid.`. oωo provides a way to reduce this repetition by allowing you to nest translations. Instead of writing out the entire path of each and every key, you simply nest the unique parts of each key inside an object with the common affix as the key and a `{}` where the unique part goes. For example, the above can be written as: en\_us.json json { "owo:nested_lang": true, "item.modid.{}": { "firstItem": "First Item", "secondItem": "Second Item", "thirdItem": "Third Item", "fourthItem": "Fourth Item", "fifthItem": "Fifth Item", "sixthItem": "Sixth Item", "seventhItem": "Seventh Item", "eighthItem": "Eighth Item", "ninthItem": "Ninth Item", "tenthItem": "Tenth Item" } } If you want to be even crazier you can provide a prefix **and** a suffix, like so: en\_us.json json { "owo:nested_lang": true, "item.modid.{}Item": { "first": "First Item", "second": "Second Item", "third": "Third Item", "fourth": "Fourth Item", "fifth": "Fifth Item", "sixth": "Sixth Item", "seventh": "Seventh Item", "eighth": "Eighth Item", "ninth": "Ninth Item", "tenth": "Tenth Item" } } You can also provide _only_ a suffix, although our example isn't exactly a great use case for it: en\_us.json json { "owo:nested_lang": true, "{}Item": { "item.modid.first": "First Item", "item.modid.second": "Second Item", "item.modid.third": "Third Item", "item.modid.fourth": "Fourth Item", "item.modid.fifth": "Fifth Item", "item.modid.sixth": "Sixth Item", "item.modid.seventh": "Seventh Item", "item.modid.eighth": "Eighth Item", "item.modid.ninth": "Ninth Item", "item.modid.tenth": "Tenth Item" } } Array Nesting [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#array-nesting) -------------------------------------------------------------------------------------------- Certain situations may call for indexed lists of keys, for example: en\_us.json json { "item.modid.overlyToolTippedItem": "Overly Tool Tipped Item", "item.modid.overlyToolTippedItem.tooltip.1": "This is the first line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.2": "This is the second line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.3": "This is the third line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.4": "This is the fourth line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.5": "This is the fifth line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.6": "This is the sixth line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.7": "This is the seventh line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.8": "This is the eighth line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.9": "This is the ninth line of the tooltip", "item.modid.overlyToolTippedItem.tooltip.10": "This is the tenth line of the tooltip" } This is far too much text to have to write out 10 times, so instead you can use the array syntax: en\_us.json json { "owo:nested_lang": true, "item.modid.overlyToolTippedItem.{}": { "": "Overly Tool Tipped Item", "tooltip.{}": [ \ "This is the first line of the tooltip",\ "This is the second line of the tooltip",\ "This is the third line of the tooltip",\ "This is the fourth line of the tooltip",\ "This is the fifth line of the tooltip",\ "This is the sixth line of the tooltip",\ "This is the seventh line of the tooltip",\ "This is the eighth line of the tooltip",\ "This is the ninth line of the tooltip",\ "This is the tenth line of the tooltip"\ ] } } Note An empty string key `""` will strip any trailing non-alphanumeric characters from the prefix, in this case the trailing period. By default, the indexing will start at 1, but you may specify a different starting index by adding a number between the curly braces, like so: em.json json { "owo:nested_lang": true, "item.modid.overlyToolTippedItem.tooltip.{5}": [ \ "This is the fifth line of the tooltip",\ "This is the sixth line of the tooltip",\ "This is the seventh line of the tooltip",\ "This is the eighth line of the tooltip",\ "This is the ninth line of the tooltip",\ "This is the tenth line of the tooltip"\ ] } Nested Nesting [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#nested-nesting) ---------------------------------------------------------------------------------------------- Nested Keys can also be nested, for example: en\_us.json json { "owo:nested_lang": true, "item.modid.{}": { "firstItem": "First Item", "secondItem": { "": "Second Item", "tooltip.{}": [ \ "This is the first line of the tooltip",\ "This is the second line of the tooltip",\ "This is the third line of the tooltip"\ ] } } } This will produce the following keys: en\_us.json json { "item.modid.firstItem": "First Item", "item.modid.secondItem": "Second Item", "item.modid.secondItem.tooltip.1": "This is the first line of the tooltip", "item.modid.secondItem.tooltip.2": "This is the second line of the tooltip", "item.modid.secondItem.tooltip.3": "This is the third line of the tooltip" } Rich Translations [​](https://docs.wispforest.io/owo/data-extensions/nested-lang#rich-translations) ---------------------------------------------------------------------------------------------------- Nested keys work perfectly with [Rich Translations](https://docs.wispforest.io/owo/data-extensions/rich-translations) , for example this: en\_us.json json { "item.minecraft.echo_shard": [\ "Echo ",\ { "text": "Shard", "color": "#0096FF" }\ ], "item.minecraft.recovery_compass": [\ "",\ { "text": "Recovery Compass", "color": "yellow" },\ " made of ",\ { "translate": "item.minecraft.echo_shard" }\ ] } can be simplified to: en\_us.json json { "owo:nested_lang": true, "item.minecraft.{}": { "echo_shard": [\ "Echo ",\ { "text": "Shard", "color": "#0096FF" }\ ], "recovery_compass": [\ "",\ { "text": "Recovery Compass", "color": "yellow" },\ " made of ",\ { "translate": "item.minecraft.echo_shard" }\ ] } } This page was brought to you by chyzman --- # Item Groups | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/item-groups#VPContent) On this page Item Groups [​](https://docs.wispforest.io/owo/item-groups#item-groups) ======================================================================== Versioning Note This guide is only applicable for Minecraft versions 1.19.3 and onwards. There were significant changes to the item group system which mandated changes in owo's approach to stay compatible. Creating a basic owo item group is easy - begin by calling `OwoItemGroup.builder(...)` and supply it with: * The identifier to register your group with * A function that creates the icon of your group - this is called at a later stage during the initialization process and should use one of the `Icon.of(...)` overloads java public static final OwoItemGroup GROUP = OwoItemGroup .builder(new Identifier("mod-id", "item_group"), () -> Icon.of(Mod.ITEM)) // additional builder configuration goes between these lines .build(); ### Adding tabs [​](https://docs.wispforest.io/owo/item-groups#adding-tabs) In order to add a tab to your item group, extend your group builder configuration with the `intializer(...)` method. Inside it, call `group.addTab(...)` - this accepts four parameters: | Parameter | Description | | --- | --- | | `icon` | The icon of this tab. Look at the different methods available on the `Icon` class, as this supports more than just items | | `name` | The name of this tab, used for the translation key | | `contentTag` ⠀ | The item tag used for populating the content of this tab. If you wish to populate your tabs in code using `OwoItemSettings.tab(...)`, this may be `null` | | `primary` | If this is `true`, the tab's name is displayed as-is, otherwise the name of the item group itself gets prepended | About custom tabs If you want or need more precise control over how the tab is populated, you can use the `group.addCustomTab(...)` function instead. Instead of a tag, it takes a `ContentSupplier` function that is called and provided the `ItemGroup.Entries` to append to when the creative inventory is initialized ### Adding buttons [​](https://docs.wispforest.io/owo/item-groups#adding-buttons) Buttons work in much the same way as tabs, except that they usually require fewer parameters. Given that most of the time you would want to link to some external resource, `ItemGroupButton.link(...)` and the related methods like `ItemGroupButton.modrinth(...)` should be of interest. You can then pass this button directly into the `group.addButton(...)` method. If you want your button to execute a custom action, simply call the constructor directly. It accepts the following parameters: | Parameter | Description | | --- | --- | | `group` | The item group the button belongs to, usually just `group` | | `icon` | The icon of this button, this works identically as it does for tabs | | `name` | The name of this button, used for the translation key | | `action` | The action to run when this button is pressed | ### Configuring the Item Group further [​](https://docs.wispforest.io/owo/item-groups#configuring-the-item-group-further) #### Using a custom texture [​](https://docs.wispforest.io/owo/item-groups#using-a-custom-texture) In order to change the texture used to render your item group, insert `customTexture(...)` with the ID of the texture you want to use. This texture needs to follow a specific format in order to look correct, for this you can use the [template included in the testmod](https://github.com/wisp-forest/owo-lib/blob/1.19.3/src/testmod/resources/assets/uwu/textures/gui/group.png) . #### Configuring stacking height [​](https://docs.wispforest.io/owo/item-groups#configuring-stacking-height) The buttons to either side of your item group (that is, tabs on the left and buttons on the right), usually arrange themselves to stack up to a height of 4. Depending on how many tabs or buttons you have, it may make sense to change this. To do so, insert either `tabStackHeight(...)` or `buttonStackHeight(...)`. #### Making the title static [​](https://docs.wispforest.io/owo/item-groups#making-the-title-static) You can insert `disableDynamicTitle()` to force the title of your item group to be static, instead of changing with the tab that is selected. ### Initializing the group [​](https://docs.wispforest.io/owo/item-groups#initializing-the-group) After all your items have been registered, it is very important that you call the `initialize()` method on your item group. This will run setup and create the group icon, both of which can cause trouble if executed earlier. ### Using custom stack generators [​](https://docs.wispforest.io/owo/item-groups#using-custom-stack-generators) When using `OwoItemSettings` instead of the vanilla `Item.Settings` to configure your item's tabs, you may have noticed the `stackGenerator(...)` setter. Using this, you can change the function which appends your item to the item group. The default function will just call `getDefaultStack()` on your item, which may not always be sufficient. Particularly if you want variations of your item with different NBT data, changing this may prove useful --- # Endecs | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/endec#VPContent) On this page Endecs [​](https://docs.wispforest.io/owo/endec#endecs) ======================================================== Outdated Documentation This version of the Endec documentation, while still communicating the correct concepts, does no longer directly apply to the versions of the API which is presently available in owo. If you are unsure about something, try to reference the JavaDoc or simply come ask in [our Discord](https://discord.gg/xrwHKktV2d) - we'll be happy to help The Endec API is a format-agnostic serialization framework conceptually related to [serde](https://serde.rs/) and interoperable with Mojang's own Codec API (found in [DFU](https://github.com/Mojang/DataFixerUpper) and used throughout the vanilla codebase). Given the endec for a certain type, it can be serialized to and from any format with a (`De`)`Serializer` implementation. Importantly, endecs can serialize to both sequential and hierarchical formats - this means that, contrary to DFU's Codec, **endecs can be used directly for networking** without needing an intermediary format like NBT and wasting a bunch of space on structure information. The Data Model [​](https://docs.wispforest.io/owo/endec#the-data-model) ------------------------------------------------------------------------ The framework models all possible serialized formats using the following 11 primitive and 4 compound types which each (de)serializer must support. This table lists the types with their representations in NBT and JSON: | Type | Description | NBT | JSON | | --- | --- | --- | --- | | byte | Signed 8-bit integer | NbtByte | JsonPrimitive | | short | Signed 16-bit integer | NbtShort | JsonPrimitive | | int | Signed 32-bit integer | NbtInt | JsonPrimitive | | var\_int | Signed 32-bit [variable-length](https://wiki.vg/VarInt_And_VarLong)
integer | NbtInt | JsonPrimitive | | long | Signed 32-bit integer | NbtLong | JsonPrimitive | | var\_long | Signed 64-bit [variable-length](https://wiki.vg/VarInt_And_VarLong)
integer | NbtLong | JsonPrimitive | | float | Single-precision floating point number | NbtFloat | JsonPrimitive | | double | Double-precision floating point number | NbtDouble | JsonPrimitive | | boolean | true or false | NbtByte | JsonPrimitive | | string | A UTF-8 character sequence | NbtString | JsonPrimitive | | bytes | A sequence of bytes | NbtByteArray | JsonArray | | optional | A single value or nothing | NbtCompound | JsonElement \| JsonNull | | sequence | Sequence of values, each of the same type | NbtList | JsonArray | | map | Mapping from strings to single values | NbtCompound | JsonObject | | struct | Mapping from strings to single values with all keys statically known | NbtCompound | JsonObject | Optional fields in JSON and NBT For both JSON and NBT, when the optional being serialized is the field of a struct, the field will be omitted to indicate an empty optional and simply be the value otherwise Using Endecs [​](https://docs.wispforest.io/owo/endec#using-endecs) -------------------------------------------------------------------- The framework comes with a number of built-in endecs ready for you to use - the 11 primitive types from the data model accessible as constants on the `Endec` interface and the rest in the `BuiltInEndecs` class. Additionally, any DFU codec, vanilla or modded, can be used through `Endec.ofCodec` Now, say we want to encode a single `BlockPos` as JSON. For this, we use the endec supplied in `BuiltInEndecs`, the `JsonSerializer` class and the `Endec#encodeFully` method - like this: java var pos = new BlockPos(1, 2, 3); JsonElement result = BuiltInEndecs.BLOCK_POS.encodeFully(JsonSerializer::of, pos); If you now printed this result, it'd look something like this: `[1,2,3]`. Next, let's turn this JSON back into a `BlockPos`. For this we use the same endec as we used for encoding, the `JsonDeserializer` class and the `Endec#decodeFully` method: java BlockPos decoded = BuiltInEndecs.BLOCK_POS.decodeFully(JsonDeserializer::of, result); Unlike DFU, if an endec encounters an error while decoding it throws an exception - so make sure you catch those if you're deserializing unknown data. Building Endecs [​](https://docs.wispforest.io/owo/endec#building-endecs) -------------------------------------------------------------------------- Of course, often times you'll be serializing your own types for which no endec exists yet - thus we must also know how to construct ones of our own. ### Basic compound types [​](https://docs.wispforest.io/owo/endec#basic-compound-types) The 3 simple trivial compound types from the data model all have direct operators on each endec. For example, we can easily turn an `Endec` into an `Endec>` with `Endec#listOf()`. Generally: | Method | Type Signature | | --- | --- | | Endec#listOf | `Endec` 🠖 `Endec>` | | Endec#mapOf | `Endec` 🠖 `Endec>` | | Endec#optionalOf | `Endec` 🠖 `Endec>` | Using these, you can realize a lot of basic data structures. For maps with non-string keys, you have two options * If your keys have a good string representation, use `Endec.map(Function, Function, Endec)` which encodes to a proper data model map by serializing each key using the provided `keyToString` and `keyFromString` functions. For example, for a map from `Identifier` to `boolean`, you might do something like this: java Endec> endec = Endec.map(Identifier::toString, Identifier::new, Endec.BOOLEAN); * Otherwise, use `Endec.map(Endec, Endec)` and supply an endec for your keys. Since the data model cannot represent a map like this without somehow requiring a string representation for the keys, this instead serializes to a list of key-value pairs. This example is quite contrived but still demonstrates the principle: java Endec> endec = Endec.map(BuiltInEndecs.BLOCK_POS, BuiltInEndecs.IDENTIFIER); ### Structs [​](https://docs.wispforest.io/owo/endec#structs) Assume we have the following class which we wish to serialize: java public class FabledBananasClass { private final int bananaAmount; private final Item bananaItem; private final List bananaPositions; public FabledBananasClass(int bananaAmount, Item bananaItem, List bananaPositions) {...} public int bananaAmount() { return this.bananaAmount; } public Item bananaItem() { return this.bananaItem; } public List bananaPositions() { return this.bananaPositions; } } For this, we want to use the 4th compound type from the data model - structs. There are two reasons for this: For one, the API is specifically designed for this and thus very easy to use - but also, binary formats like the `ByteBufSerializer` (used for networking) can omit the field names and save a bunch of space this way over just using a map. To create an endec for a struct, we first need endecs for all its fields. In this case, those are reasonably simple to obtain: * For `bananaAmount` we use `Endec.INT` * Since the values of `bananaItem` are stored in the item registry, we can serialize them as their identifiers using `BuiltInEndecs.ofRegistry(Registries.ITEM)` * Finally, for `bananaPositions` we use the `listOf` operator from above: `BuiltInEndecs.BLOCK_POS.listOf()` Now we may proceed using the aptly named `StructEndecBuilder` and the `fieldOf` operator which turns an endec into a struct field specification: java Endec thisEndecIsBananas = StructEndecBuilder.of( Endec.INT.fieldOf("banana_amount", FabledBananasClass::bananaAmount), BuiltInEndecs.ofRegistry(Registries.ITEM).fieldOf("banana_item", FabledBananasClass::bananaItem), BuiltInEndecs.BLOCK_POS.listOf().fieldOf("banana_positions", FabledBananasClass::bananaPositions), // up to 17 fields can be declared here (1) FabledBananasClass::new ); 1. It's 17 specifically because that is one more than DFU supports. Take that! For each field, we specify the name to use in the serialized representation and a getter function the endec should use for getting the value of that field from an instance of the class. Also, we supply a constructor the endec uses for instantiating the class when decoding, which accepts the same fields in the same order as declared. If you wanted any of these fields to be optional, you would use the `optionalFieldOf` operator for that field instead and supply a default value to use when the field is missing from the serialized data. Optional fields are not always supported Optional fields are generally only supported by self-described formats. The packet buffer format, for example, does not support optional fields since it omits the fields names and thus cannot tell whether a field is missing. Should a field be missing there, deserialization failure will generally result. However, since those formats are not usually authored by humans, this should never be an issue in practice * * * The structure and bananas of this guide are somewhat inspired by [Evelyn](https://enjarai.dev/#/) 's excellent [Codec guide](https://docs.fabricmc.net/develop/codecs) --- # Registration | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/registration#VPContent) On this page Registration [​](https://docs.wispforest.io/owo/registration#registration) =========================================================================== TIP The further down this article goes, the more advanced it becomes and the less likely it is you will need the information for a simple mod. Basics [​](https://docs.wispforest.io/owo/registration#basics) --------------------------------------------------------------- ^0.1.3 oωo offers a flexible system for automatically registering things into `Registry` instances. It revolves around the idea of storing the values to be registered into `public static` fields of a class. Consider this example: java public class ItemInit { public static final Item BLACKBERRY = new Item(...); public static final Item BANANA = new Item(...); ... public static void init() { Registry.register(Registry.ITEM, Mod.id("blackberry"), BLACKBERRY); Registry.register(Registry.ITEM, Mod.id("banana"), BANANA); ... } } Because all this class does is register each of its fields into the `ITEM` registry, it can be easily shortened by implementing `ItemRegistryContainer` java public class ItemInit implements ItemRegistryContainer { public static final Item BLACKBERRY = new Item(...); public static final Item BANANA = new Item(...); ... public static final String notAnItem = "this will be ignored"; ... } As you can see, basically all boilerplate was eliminated and the code is now entirely declarative. Now you might be wondering when your items will be registered. Luckily, you still get to keep full control of that. Instead of calling `init()`, you now use the `FieldRegistrationHandler`. java public class ModInit implements ModInitializer { public static final String MOD_ID = "based"; @Override public void onInitialize() { ... // before ItemInit.init(); ... ... // after FieldRegistrationHandler.register(ItemInit.class, MOD_ID, false); ... } } All the fields of your `ItemInit` class will now be registered the moment you call this method. Their IDs will be determined by the name of the field prefixed with the namespace you passed to the handler - e.g. the `BANANA` in this example would be registered as `based:banana`. The sneaky `String` inside the `ItemInit` class will be ignored - in fact, any field that does not match or extend the type you're registering will be. ### Blocks [​](https://docs.wispforest.io/owo/registration#blocks) Blocks are a bit of a special case because each block usually also requires a `BlockItem`. Conveniently, the `BlockRegistryContainer` takes care of that. You can just implement it without declaring any methods, however you won't get any control over the generated `BlockItem`s that way. As such, it is normally a good idea to also implement `createBlockItem(...)` in your class. java public class BlockInit implements BlockRegistryContainer { public static final Block CLEAR_GLASS = new Block(...); @NoBlockItem // available since 0.3.13+1.18 public static final Block DEBUG_BLOCK = new Block(...); @Override public BlockItem createBlockItem(Block block, String identifier) { return new BlockItem(block, new Item.Settings().group(Mod.MOD_GROUP)); } } As you can see, you get full control over the BlockItem which is created. You might also have noticed the `@NoBlockItem` annotation on the debug block. It simply tells the registration system that this specific block should not have an item created for it. ### Non-Standard types [​](https://docs.wispforest.io/owo/registration#non-standard-types) Up to this point, we've been using the pre-defined implementations of `AutoRegistryContainer` that oωo provides. These don't exist for all registries however, thus we need to make our own. Implementing this interface is trivial though, we only need to provide the type of field to register as well as the matching `Registry`. java public class BlockEntityInit implements AutoRegistryContainer> { public static final BlockEntityType MOD_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(...).build(); @Override public Registry> getRegistry() { return Registry.BLOCK_ENTITY_TYPE; } @Override @SuppressWarnings("unchecked") public Class> getTargetFieldType() { return (Class>) (Object) BlockEntityType.class; } } You may notice that there is some nasty casting going on inside `getTargetFieldType()`. You can safely copy and ignore it should you need it yourself, it is simply here to talk the compiler into doing its job. ### Running extra work after registration [​](https://docs.wispforest.io/owo/registration#running-extra-work-after-registration) Should you have some work you would have usually executed at the end of you `init()` method, you can implement the `afterFieldProcessing()` method on your container class. java public class BlockInit implements BlockRegistryContainer { ... @Override public void afterFieldProcessing() { // do extra work here } } Advanced Usage [​](https://docs.wispforest.io/owo/registration#advanced-usage) ------------------------------------------------------------------------------- ### Annotations [​](https://docs.wispforest.io/owo/registration#annotations) java @AssignedName This annotation may be used if you wish your field to have a different name than the one it has in code. Refer to this example: java public class ItemInit implements ItemRegistryContainer { public static final Item BANANA = new Item(...); // registered as 'mod_id:banana' @AssignedName("apple") public static final Item ORANGE = new Item(...); // registered as 'mod_id:apple' instead of 'mod_id:orange' } * * * java @IterationIgnored Signals the `FieldRegistrationHandler` that the annotated field should be ignored when processing the class. Refer to this example: java public class ItemInit implements ItemRegistryContainer { public static final Item BANANA = new Item(...); @IterationIgnored public static final Item NOT_REGISTERED = new Item(...); } ### Inner classes [​](https://docs.wispforest.io/owo/registration#inner-classes) Should you want to organize your container class with subclasses, you're in luck as the system fully supports this scheme. In fact, there is an intended use-case - registering fields into different namespaces. To make use of this, create an inner class which implements the same type of registry container as the outer one. Then, annotate it with the `@RegistryNamespace` annotation to override the namespace of the generated identifiers. java public class ItemInit implements ItemRegistryContainer { // registered as 'mod_id:banana' public static final Item BANANA = new Item(...); @RegistryNamespace("inner_class") public static class Inner implements ItemRegistryContainer { // registered as 'inner_class:damascus_ingot' public static final Item DAMASCUS_INGOT = new Item(...); } } Finally, to make this work, change the last argument in the call to `FieldRegistrationHandler.register(...)` to `true`. This tells it to also search the inner classes of the target class, which we usually keep disabled. ### Non-registry targets [​](https://docs.wispforest.io/owo/registration#non-registry-targets) As the field-traversal system used by the `AutoRegistryContainer` is generic and exposed as part of oωo's API, you can also use to iterate the fields of something non-registry related. Say, for example, you had a class which does many complex things and that needs to be initialized. Further imagine you had multiple instances of it stored in the fields of a container class. You could then implement `SimpleFieldProcessingSubject` on that container class and initialize them all at once. java public class ComplexInit implements SimpleFieldProcessingSubject { public static final ComplexThing A_COMPLEX_THING = new ComplexThing(...); public static final ComplexThing ANOTHER_COMPLEX_THING = new ComplexThing(...); ... @Override public void processField(ComplexThing thing, String identifier, Field field) { thing.init(); } @Override public Class getTargetFieldType() { return ComplexThing.class; } } To then execute this code, simply use the `FieldRegistrationHandler` java FieldRegistrationHandler.processSimple(ComplexInit.class, false); For further information on how this system works and what it can do, take a look at the [javadoc](https://docs.wispforest.io/javadoc/owo/io/wispforest/owo/registration/reflect/package-summary.html) for the `io.wispforest.owo.registration.reflect` package. ### Determining block item settings with annotations [​](https://docs.wispforest.io/owo/registration#determining-block-item-settings-with-annotations) ^0.3.13+1.18 Sometimes, you might want to change the `Item.Settings` of a block item only for some blocks in your `BlockRegistryContainer`. If this applies to one or two of them, a simple `if (...)` in the `createBlockItem` method should suffice, but if a more flexible system is desired you can override `postProcessField(...)` in your container class. This method has a default implementation that simply calls `createBlockItem`, but we won't do that anymore and instead directly create the item in there. This has the advantage of having the `Field` object available, so annotation lookups are possible. First, let's define our annotation, in its own class file: java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NoItemGroup {} This is just a simple annotation with no value that can only applied to a field and that is retained for runtime-access, unlike the default `SOURCE` policy. We can then use it to annotate a field and check for its presence in our overridden `postProcessField(...)` method. java public class BlockInit implements BlockRegistryContainer { public static final Block CLEAR_GLASS = new Block(...); @NoItemGroup public static final Block DEBUG_BLOCK = new Block(...); @Override public void postProcessField(String namespace, Block value, String identifier, Field field) { // preserve normal traversal behaviour if (field.isAnnotationPresent(NoBlockItem.class)) return; // create basic item settings var settings = new Item.Settings(); // check if our annotation is present and only // assign the itemgroup if it is not if (!field.isAnnotationPresent(NoItemGroup.class)){ settings.group(Mod.MOD_GROUP) } // finally, create and register the block item Registry.register(Registry.ITEM, new Identifier(namespace, identifier), new BlockItem(value, settings))); } } This is only a simple example, although it should make it clear how this system can be expanded to cover more of the items' properties. --- # Networking | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/networking#VPContent) On this page Networking [​](https://docs.wispforest.io/owo/networking#networking) ===================================================================== ^0.7.0 Networking can be quite the hassle to set up and maintain when using only the tools provided to you by Minecraft and Fabric API. owo, however, includes an easy-to-use networking stack which is quick to set up and even easier to maintain while still keeping high performance. Concepts [​](https://docs.wispforest.io/owo/networking#concepts) ----------------------------------------------------------------- owo's networking API comprises of two core systems - the `OwoNetChannel`, which handles all networking, and the `Endec` serialization framework, which takes care of serializing objects into packet buffers. For safety reasons, using a channel in your mod will enable owo's handshaking procedure. This verifies that the channel layout on both server and client is identical, to prevent possible crashes or malformed data received, if one side is encoding differently than the other expects. Implementation [​](https://docs.wispforest.io/owo/networking#implementation) ----------------------------------------------------------------------------- ### Creating your channel [​](https://docs.wispforest.io/owo/networking#creating-your-channel) To get started creating your first channel, simply create a new instance of `OwoNetChannel` and store it in a constant field somewhere: java ... // as long as you use your mod's id here, the path doesn't actually // matter - it really could be anything public static final OwoNetChannel MY_CHANNEL = OwoNetChannel.create(new Identifier("my_mod", "main")); ... ### Adding the first packet [​](https://docs.wispforest.io/owo/networking#adding-the-first-packet) Packets sent using `OwoNetChannel` are declared as records - they're plain data-carriers. A simple packet might look like this: java public record MyPacket(int someData, String otherData, Identifier aMinecraftClass) {} This now needs to be registered with your channel. To do this, you have two options: * #### Direct Registration [​](https://docs.wispforest.io/owo/networking#direct-registration) This needs to happen during general mod initialization (that is, most of the time in the class which implements `ModInitializer`) and is generally useful for packets which are either serverbound or whose handles don't reference client-only things in a way that would break a server. To register a packet in this style, use either the `registerClientbound` or `registerServerbound` method on your channel. These methods are named after where the packet is sent to, e.g. `registerServerbound` is used for packets sent _from_ the client _to_ the server. java public class MyModInitializer implements ModInitializer { public static final OwoNetChannel MY_CHANNEL = OwoNetChannel.create(new Identifier("my_mod", "main")); @Override public void onInitialize() { MY_CHANNEL.registerServerbound(MyPacket.class, (message, access) -> { // server-safe handler code goes here }); } } * #### Deferred Registration [​](https://docs.wispforest.io/owo/networking#deferred-registration) Same as direct registration, this has to happen during mod initialization. It may however, happen during different stages. This is useful for clientbound packets whose handles cannot be safely classloaded on a server. To register a packet in deferred mode, call `registerClientboundDeferred` during general mod initialization and `registerClientbound` during client mod initialization. General InitializerClient Initializer java public class MyModInitializer implements ModInitializer { public static final OwoNetChannel MY_CHANNEL = OwoNetChannel.create(new Identifier("my_mod", "main")); @Override public void onInitialize() { MY_CHANNEL.registerClientboundDeferred(MyPacket.class); } } java public class MyClientModInitializer implements ClientModInitializer { @Override public void onInitializeClient() { MY_CHANNEL.registerClientbound(MyPacket.class, (message, access) -> { // arbitrary handler code goes here }); } } Now there are two important observations to be made about this code we just wrote: 1. We did not write any serialization code, nor did we register any serializers. All primitives, a lot of Java utility classes and most if not all relevant Minecraft utility classes are supported out of the box 2. Because serialization is done for us, we do not need to manually schedule our processing on the client or server thread - this is handled by the channel directly You might also wonder what the two parameters in the handler represent. It's quite straight forward: `message` is simply the deserialized packet (a `MyPacket` instance in the example) and `access` is short for `EnvironmentAccess` - it lets you grab the player which received the packet as well as the client or server in the form of the `runtime` property. ### Sending the first packet [​](https://docs.wispforest.io/owo/networking#sending-the-first-packet) In order to send a packet via an `OwoNetChannel`, you need what we call a `Handle`. This is an object bound to a certain target, with a `send` method which sends packets to that target. To bind and obtain a `Handle`, use either the `clientHandler()` or any of the `serverHandle(...)` methods. A handle is always named after where it is sending _from_, thus a `ServerHandle` sends _from_ the server _to_ the client and vice-versa. Attention The server and client handles are re-used. Each channel stores exactly one handle of each kind and binds it every time you request it via any of the methods. This means you _cannot_ store a handle for later usage - it is not in any way guaranteed to still be bound to the same target and could thus cause all kinds of weird and undefined behavior. Once you obtained a handle, you can use it to send a packet: Server ExampleClient Example java // to send to a certain player var player = ; MyModInitializer.MY_CHANNEL.serverHandle(player).send(new MyPacket(1, "this", new Identifier("is", "podge"))); // to send to all players watching a block entity var blockEntity = ; MyModInitializer.MY_CHANNEL.serverHandle(blockEntity).send(new MyPacket(1, "this", new Identifier("is", "podge"))); java MyModInitializer.MY_CHANNEL.clientHandle().send(new MyPacket(1, "this", new Identifier("is", "podge"))); ### Registering Custom Endec [​](https://docs.wispforest.io/owo/networking#registering-custom-endec) There may be a point where a given message may contain object types that cannot be reflectively built using the `ReflectiveEndecBuilder`. In this case, you need to register custom `Endec`(s). This can be accomplished by calling the channel's `addEndecs(...)` method, which accepts a callback that can register additional `Endec`s to the channel's endec builder. java public record MyPacket(int index, String name, Identifier target, @Nullable List additionalNames) { public static final StructEndec ENDEC = StructEndecBuilder.of( Endec.INT.fieldOf("index", MyPacket::index), Endec.STRING.fieldOf("name", MyPacket::name), MinecraftEndecs.IDENTIFIER.fieldOf("target", MyPacket::target), Endec.STRING.listOf().nullableOf().fieldOf("additional_names", MyPacket::additionalNames), MyPacket::new ); } MyModInitializer.MY_CHANNEL.addEndecs(builder -> { builder.register(MyPacket.ENDEC, MyPacket.class); }); --- # Features | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/features#VPContent) On this page Features [​](https://docs.wispforest.io/owo/features#features) =============================================================== [Registration](https://docs.wispforest.io/owo/registration) [​](https://docs.wispforest.io/owo/features#registration) ----------------------------------------------------------------------------------------------------------------------- ^0.1.3 oωo offers a flexible system for automatically registering a class' fields into `Registry` instances. It is very quick to implement and usually eliminates a lot of boilerplate. At the same time though, it is extremely flexible and allows significant customization and complete control over how the fields are processed using the `FieldProcessingSuject` API tree. Networking [​](https://docs.wispforest.io/owo/features#networking) ------------------------------------------------------------------- ^0.4.0 oωo provides a fully-featured network serialization system. Built around `OwoNetChannel` and the `Endec` framework, packet data is automatically (de-)serialized and the corresponding handlers invoked without the need of keeping track of Identifiers or channel associations - everything is derived from the data class. Packet contents are defined as Java `record`s, making it highly ergonomic. The serialization backend fully supports serializers for custom types and, in development, all channel-layout related data is synced between client and server making it so you cannot forget to register a handler on one side. [Item Groups](https://docs.wispforest.io/owo/item-groups) [​](https://docs.wispforest.io/owo/features#item-groups) -------------------------------------------------------------------------------------------------------------------- ^0.2.0 The Item Group API allows you to create an Item Group with custom textures and sub-tabs in very few lines of code. It is still pretty open to customization and can make custom buttons for linking to your mod's project pages or even executing custom actions relevant to your mod. Particle Effects [​](https://docs.wispforest.io/owo/features#particle-effects) ------------------------------------------------------------------------------- ^0.1.0 oωo provides two main systems for handling particles. Primarily there's a client-sided set of utility methods for displaying multiple particles in the world, including drawing lines and cubes, randomized velocity and other general utility. To then allow easily composing and triggering these methods from the server, the `ParticleSystem` API, built on top of oωo's networking stack, can be used. Debug/Dev Features [​](https://docs.wispforest.io/owo/features#debug-dev-features) ----------------------------------------------------------------------------------- ^0.3.0 When in a development environment, oωo's debug mode is automatically enabled which adds a host of features like commands for damaging/healing the player or dumping information about game objects, automatically **disabled weather and daylight cycle** and a few more. Moddata [​](https://docs.wispforest.io/owo/features#moddata) ------------------------------------------------------------- ^0.3.0 The `ModDataLoader` and related API are designed to allow loading JSON-formatted data from the `mod` directory of all present mods. This makes it very easy to utilize datapack-like files for data that needs to be available before a world is loaded. For the purposes of modpack customization, the files are loaded from `.minecraft/moddata` as a replacement for datapacks. Screen Utilities [​](https://docs.wispforest.io/owo/features#screen-utilities) ------------------------------------------------------------------------------- ^0.3.0 oωo contains a few very simple helpers to facilitate the creation of a simple `HandledScreen` and `ScreenHandler`, namely a type of slot that only allows certain items as well as methods for generating the player inventory slots required for almost all screens. NBT Handling [​](https://docs.wispforest.io/owo/features#nbt-handling) ----------------------------------------------------------------------- ^0.7.0 The `NbtKey` class is a simple serialization wrapper that allows inserting and extracting data values from `NbtCompound` instances. It essentially wraps the `get(...)` and `put(...)` methods to eliminate magic strings and centralize serialization code. Tag Injection [​](https://docs.wispforest.io/owo/features#tag-injection) ------------------------------------------------------------------------- ^0.3.6 The `TagInjector` system allows you to inject entries into tags at runtime. There are numerous helper methods available for easily injecting blocks and items, but the barebones inject instructions are exposed as well to enable injection into Tags of arbitrary registries. Offline Data Access [​](https://docs.wispforest.io/owo/features#offline-data-access) ------------------------------------------------------------------------------------- ^0.5.0 The `OfflineDataLookup` and `OfflineAdvancementLookup` interfaces enable easily querying and/or modifying the NBT and Advancement data of offline players. As everything in oωo, the API surface is non-verbose to use and usually does not require more than a single method call. [UI Framework](https://docs.wispforest.io/owo/ui/) [​](https://docs.wispforest.io/owo/features#ui-framework) -------------------------------------------------------------------------------------------------------------- ^0.8.0 owo-ui is a declarative UI framework that helps with building dynamic screens quickly and easily. It strives to be highly embeddable, performant and, most of all, super easy to use. More information pertaining to it features and capabilities can be found within [UI section](https://docs.wispforest.io/owo/ui/) [Configuration](https://docs.wispforest.io/owo/config/) [​](https://docs.wispforest.io/owo/features#configuration) -------------------------------------------------------------------------------------------------------------------- ^0.8.0 oωo provides a highly flexible annotation-driven configuration system. It aims to be simple yet powerful and offers a wide range of customizability and features as discussed within the [Config section](https://docs.wispforest.io/owo/config/) Endec [​](https://docs.wispforest.io/owo/features#endec) --------------------------------------------------------- ^0.12.0 [endec](https://github.com/wisp-forest/endec) is a format-agnostic serialization framework inspired by Rust's [serde](https://serde.rs/) library and the Codec API from Mojang's [DataFixerUpper](https://github.com/mojang/datafixerupper) . More information on its features and implementation details can be found within the [Endec Section](https://docs.wispforest.io/owo/endec) --- # System Properties | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/system-properties#VPContent) On this page System Properties [​](https://docs.wispforest.io/owo/system-properties#system-properties) ========================================================================================== oωo exposes a number of [Java System Properties](https://stackoverflow.com/a/7054981) which allow enabling/disabling some runtime features of the library. This page outlines which properties there are and what they do ### Properties [​](https://docs.wispforest.io/owo/system-properties#properties) #### `-Dowo.handshake.disable ` [​](https://docs.wispforest.io/owo/system-properties#dowo-handshake-disable-true-false) Completely disables oωo's handshaking procedure, thereby allowing you to join servers which would otherwise be wrongly detected as incompatible. If the handshaking channel can still be opened, like if you don't have a proxy or are using the [oωo velocity plugin](https://github.com/BasiqueEvangelist/OwoVelocityPlugin) , the server's setting is authoritative and even a client with handshake enabled will still be able to join. If the channel is unavailable for any reason, both the client _and_ server need to have this flag set to `true` in order to connect successfully. * * * #### `-Dowo.debug ` [​](https://docs.wispforest.io/owo/system-properties#dowo-debug-true-false) Enables oωo's debug mode. In this mode, oωo itself registers a number of additional commands helpful for debugging purposes and tweaks the game's behavior in some other ways to ease development. This state of this flag is part of the API surface as well, so expect other mods that use oωo to toggle their own debug features based on it. * * * #### `-Dowo.forceDisableDebug ` [​](https://docs.wispforest.io/owo/system-properties#dowo-forcedisabledebug-true-false) Only available in development environments. Because debug mode is enabled by default in those, you can use this flag to toggle it off in case you need to test something without debug features enabled. * * * #### `-Dowo.sentinel.forceHeadless ` [​](https://docs.wispforest.io/owo/system-properties#dowo-sentinel-forceheadless-true-false) Forces oωo-sentinel to always use its command-line-based mode instead of opening a window, even if a graphical desktop environment is detected --- # owo-ui Academy | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/academy#VPContent) On this page owo-ui Academy [​](https://docs.wispforest.io/owo/ui/academy#owo-ui-academy) ============================================================================= ![owo-ui-academy example screen](https://docs.wispforest.io/assets/ui-academy.CA9OaSKV.png) owo-ui-academy is an interactive, in-game tutorial that teaches you the essential concepts of owo-ui. #### Setup [​](https://docs.wispforest.io/owo/ui/academy#setup) Given that it is a developer tool, builds are not publicly hosted - you need to get them from [GitHub Actions](https://github.com/wisp-forest/owo-ui-academy/actions) . Alternatively you may also clone the repository and run it, just like any other mod. Once you have the mod installed, simply join a world and press the keybind helpfully noted in the little notification. #### Using the Inspector [​](https://docs.wispforest.io/owo/ui/academy#using-the-inspector) While playing around with owo-ui-academy, the inspector is your most important tool. It can visualize what's internally going on in the playground and also how the tutorial screens themselves are laid out. Apart from the standard inspector, you can also press Alt+Shift to bring it into _global mode_, where it will draw the debug overlay for every component on screen. --- # Button | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/button#VPContent) Return to top Button [​](https://docs.wispforest.io/owo/ui/components/button#button) ======================================================================= ` --- # Checkbox | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/checkbox#VPContent) Return to top Checkbox [​](https://docs.wispforest.io/owo/ui/components/checkbox#checkbox) ============================================================================= `` `Components.checkbox(...)` The checkbox component represents a toggleable checkbox. It can display text alongside the checkbox. **Parameters:** * `text`: The text to display next to the checkbox. * `checked`: Whether the checkbox is checked or not. * `active`: Whether the checkbox is active or not. **Example (Code-driven):** java Components.checkbox(Text.literal("Option")) .checked(false) **Example (Data-driven):** xml Option true false --- # Setting up Hot Reloading | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/braid/hot-reloading#VPContent) On this page Setting up Hot Reloading [​](https://docs.wispforest.io/braid/hot-reloading#setting-up-hot-reloading) ====================================================================================================== One of braid's most important features is its capability to perform state-preserving hot reloads. Sadly, the design of the JVM and its debugging frameworks do not allow this to work completely out of the box and we need to do some setup. Setup [​](https://docs.wispforest.io/braid/hot-reloading#setup) ---------------------------------------------------------------- First, make sure you have standard Java hotswapping working correctly - if you're not familiar with this, check the [relevant section of the Fabric documentation](https://docs.fabricmc.net/develop/getting-started/intellij-idea/launching-the-game#hotswapping-classes) . Once you have this working, you can download the braid reload agent from [our Maven repository](https://maven.wispforest.io/#/releases/io/wispforest/braid-reload-agent) . Navigate to the folder of the latest release, download the `.jar` file and save it somewhere convenient. Then, follow the instructions in the "Hotswapping Mixins" section of the above Fabric documentation page to add it to your run configuration, substituting the Mixin Jar with the braid reload agent Jar you just downloaded. If you're already using the Mixin Java Agent, don't worry - you can have as many as you want. JetBrains Runtime [​](https://docs.wispforest.io/braid/hot-reloading#jetbrains-runtime) ---------------------------------------------------------------------------------------- As is also mentioned in the Fabric docs, most standard JVMs are very limiting in the changes they'll let you make during a hotswap. In order to get the most out of braid's hot reload feature, we recommend that you also install the JetBrains Runtime and use it whenever you're developing with braid - or just in general, as it's extremely useful! To install it, go to their [Releases section](https://github.com/JetBrains/JetBrainsRuntime/releases) , choose the latest release for the Java version you need and download the appropriate zip for your operating system and machine. Finding the one you need can be somewhat tricky, so consult the internet or feel free to ask in our Discord if you need help. Finally, unzip it into `.jdks` folder in your user directory (IntelliJ will usually already have made this for you) and select it in your project settings. You might need to restart IntelliJ once before it finds it. Finally, add the `-XX:+AllowEnhancedClassRedefinition` JVM flag in the same text field of your run config you added the `-javaagent` declaration to earlier. Verifying the Install [​](https://docs.wispforest.io/braid/hot-reloading#verifying-the-install) ------------------------------------------------------------------------------------------------ To be sure that the braid reload agent is working, launch your game and open any braid UI (if you don't have one yet, feel free of course to delay this step until you have one). You should then see a log message like the following: log [Render thread/INFO] (braid reload agent) setup complete, debounce time is 250ms This means the agent is working and ready to rebuild your UIs whenever you hot reload some widget code. --- # IntrinsicWidth and IntrinsicHeight | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/braid/intrinsic-layout#VPContent) Return to top `IntrinsicWidth` and `IntrinsicHeight` [​](https://docs.wispforest.io/braid/intrinsic-layout#intrinsicwidth-and-intrinsicheight) ================================================================================================================================= 💔 under construction --- # Layout | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/braid/layout#VPContent) On this page Layout [​](https://docs.wispforest.io/braid/layout#layout) =========================================================== braid uses a straightforward layout algorithm which is guaranteed to perform in linear time over the number of widgets. It proceeds in three stages: 1. **The parent passes constraints to its children, specifying a minimum and a maximum possible size.** When these sizes are identical on an axis, the axis is said to be _tightly constrained_. When the minimum size is zero on an axis, this axis is said to be _loosely constrained_. If the widget has no parent (i.e. it is at the root of the tree), it is tightly constrained to fill the entire screen or whichever other surface braid is rendering to. 2. **The child performs its layout calculations** (oftentimes repeating this algorithm for its own children) **and decides on a final size which it reports back to the parent.** This size must be within the constraints passed down by the parent. 3. **The parent, now knowing the exact size the child will have, sets its position.** All positions in braid are expressed in the coordinate space of the parent, which it can arbitrarily define. This nicely summarizes as follows, which anyone familiar with Flutter will no doubt recognize: > Constraints go down, sizes go up, parent sets position. Layout Widgets [​](https://docs.wispforest.io/braid/layout#layout-widgets) --------------------------------------------------------------------------- braid includes a few essential layout widgets which you'll need to know about. Assuming you've read [Getting Started](https://docs.wispforest.io/braid/getting-started) , you'll already be familiar with `Center` and `Padding`. ### `Padding` [​](https://docs.wispforest.io/braid/layout#padding) Padding inserts dead space around its child. It accepts a `Insets` object which specifies the padding for each edge separately and an optional child. If no child is provided, the widget just reserves the space dictated by the insets. When a child is provided, Padding tries to take up as much space as it needs to fit both the insets and its child. If it runs out of space trying, it will shrink the child to fit. java // insert 5 pixels of padding on all sides new Padding( Insets.all(5), new BraidLogo() ) ### `Align` and `Center` [​](https://docs.wispforest.io/braid/layout#align-and-center) Align takes an alignment and a child, grows to take up as much space as it is allowed to and aligns its child within that space as requested. The `Alignment` object specifies alignment on both axes, from 0 (meaning left on the horizontal and top on the vertical) to 1. The common alignments are provided as constants and instantiating an `Alignment` manually should rarely be necessary. When an axis is unconstrained (like is the case inside a scroll view), Align shrinks to the size of its child. Alternatively, a width and height factor can be provided which forces to the Align to be the size of its child multiplied by the factor for that axis (subject to incoming constraints of course). Center is a specialization of Align where the alignment is always center, everything else applies the same. Both alignment widgets loosen the constraints passed to their children so that they are always allowed to be smaller than the alignment widget itself. CenterGrow and alignAlign with size factors java // center the logo within the available space new Center( new BraidLogo() ) java // move the logo to the bottom right of the // available space new Align( Alignment.BOTTOM_RIGHT, new BraidLogo() ) java new Align( Alignment.TOP_LEFT, 1.5, // width factor 1.5, // height factor new BraidLogo() ) ### `Sized` and `Constrain` [​](https://docs.wispforest.io/braid/layout#sized-and-constrain) Sized accepts a width, a height (in pixels, both optional) and a child and, subject to incoming constraints, forces its child to have exactly those dimensions on the respective axes. It is a specialized version of Constrain. Constrain accepts a set of [constraints](https://docs.wispforest.io/braid/layout#layout) and a child which it passes those constraints to during layout. This can be used to, for instance, specify a range of acceptable sizes for the child. SizedConstrain java // squish the braid logo on the vertical axis new Sized( null, 32, // the braid logo is normally 64x64, so this // makes it half that size vertically new BraidLogo() ) java // force the braid logo to twice its normal size new Constrain( Constraints.only( 128.0, // minimum width 128.0, // minimum height null, // no maximum width null // no maximum height ), new BraidLogo() ) Layout Types [​](https://docs.wispforest.io/braid/layout#layout-types) ----------------------------------------------------------------------- braid comes with three major layout types out of the box: Flex, Grid and Stack. The following sections will discuss each in detail. ### `Flex`: `Row` and `Column` [​](https://docs.wispforest.io/braid/layout#flex-row-and-column) Flex widgets place their children one after another on their so-called main axis (which you must specify). The children are laid out unconstrained on the main axis and with cross-axis constraints passed down unmodified. This means that if the cross-axis is tightly constrained, the children will be too. An optional separator widget can be specified, which is placed between every child. This can be used, for instance, to insert a gap between all children with a Padding widget. Alignment on both the main and cross axis can be optionally specified, defaulting to `start` alignment for both. On the vertical axis this means top, on the horizontal axis it means left. #### Flexible Children [​](https://docs.wispforest.io/braid/layout#flexible-children) Since dropping the main axis constraints for all children is not always desired, one or multiple children can be made _flexible_ by wrapping them in a `Flexible` widget. It is essential that the path between the Flexible widget and the Flex which is supposed to contain it only contains stateless and stateful widgets - not instance widgets like `Label` or `Padding`. Flexible children are laid out after all non-flexible children, and are tightly constrained on the main axis to fill the space which remained after laying out the non-flexible children. If there is more than one flexible child, the space is divvied up between them according to their _flex factor_. For instance, if a Flex has two flexible children with flex factors 3 and 1, the one with flex factor 3 will be three times as large as the other one. Row and Column are two specializations of Flex, where the main axis is always horizontal and vertical respectively. Everything in this section applies to them. Vertical FlexRowColumn java new Flex( LayoutAxis.VERTICAL, // main axis is vertical MainAxisAlignment.END, // align to the end of the main axis, i.e. the bottom CrossAxisAlignment.STRETCH, // stretch the cross axis to its maximum possible size // and force the children to fill it Label.literal("child 1"), Label.literal("child 2") ) java new Row( // main axis is always horizontal in a row // by not specifying alignment for either axis, // we default to start alignment on both Label.literal("child 1"), Label.literal("child 2") ) java new Column( // main axis is always vertical in a row MainAxisAlignment.CENTER, // by setting the alignment to center on both axis we'll CrossAxisAlignment.CENTER, // achieve the same thing a Center widget would new Padding(Insets.all(2)), // we specify a separator (just padding in this case) to // space the children out nicely List.of( Label.literal("child 1"), // when using a separator, we must specify the children Label.literal("child 2") // in a List to differentiate the method signature and ) // tell braid which widget should be the separator ) ### `Grid` [​](https://docs.wispforest.io/braid/layout#grid) Grid widgets arrange their children into a grid where each child occupies at most one cell. Each Grid must specify a `CellFit` which decides how children are fit into their cells: If it is tight, the children receive tight constraints forcing them to fill their respective cells. If it is loose, the children receive loose constraints capped at the respective cell's size. If they end up smaller than their cell, they'll be aligned within it according to the alignment specified in the cell fit. The children of a grid are assigned to the cross axis first, up to the specified number of `crossAxisCells` after which they wrap and form a new row/column on the main axis. When a child is specified as `null`, the cell assigned to it by this algorithm is left empty. An optional cell wrapper function can be specified, which must produce a widget #### Cell Layout Details [​](https://docs.wispforest.io/braid/layout#cell-layout-details) The maximum and minimum size of the cells on each axis are dictated by that axis' respective min and max constraints divided by the number of cells which exist on the axis. Thus, when an axis receives tight constraints, each of its cells will be exactly the size of those constraints divided by the number of cells on the axis. When an axis is loosely constrained, its cells grow to accommodate the largest of their sibling cells on the opposite axis. When the cell fit is tight and axis is loosely constrained, the grid first measures how large the children in each row and column would be on their own and then passes tight constraints filling the cells to all children. Performance Considerations Measuring the sizes of all children (even on just one axis), necessitates an additional pre-layout pass (called the intrinsic layout pass) which means that the children of your grid will effectively be laid out twice for each layout iteration. See the [intrinsics page](https://docs.wispforest.io/braid/intrinsic-layout) for more information. java new Sized( // since the boxes we'll be using are unsized, 40.0, 60.0, // we set the size of the grid explicitly new Grid( LayoutAxis.VERTICAL, // main axis is vertical: 2, // thus, 2 cross-axis cells means two columns Grid.CellFit.tight(), // we force all children to fill their cells // and since the grid is tightly constrained, each // cell will be exactly 20x20 pixels new Box(Color.WHITE), // arrange some white and black boxes such that new Box(Color.BLACK), // they create a checkerboard new Box(Color.BLACK), new Box(Color.WHITE), new Box(Color.WHITE), new Box(Color.BLACK) ) ) ### `Stack` [​](https://docs.wispforest.io/braid/layout#stack) Stack accepts a list of widgets, together with an optional alignment, and places its children on top of each other (in the depth dimension) with later children on top. Constraints are passed to the children unmodified and the Stack sizes itself to its largest child. Children which end up smaller than the stack are aligned according the specified alignment, which defaults to center. #### Specifying a sizing base [​](https://docs.wispforest.io/braid/layout#specifying-a-sizing-base) A single child of a stack can be wrapped in a `StackBase` widget, which marks that widget as the definitive sizing rule for the entire stack, i.e. all other children are forced to have the same size as the base. This is useful for emulating the behavior of widgets which are placed inside one another in situations where that itself is not possible. It is essential that the path between the `StackBase` widget and the Stack which is supposed to contain it only contains stateless and stateful widgets - not instance widgets like `Label` or `Padding`. StackStack with base java // for this example to work, we assume the stack (and thus // its children) is loosely constrained new Stack( Alignment.BOTTOM_RIGHT, new Sized( Size.square(60), new Box(Color.RED) ), new Sized( Size.square(40), new Box(Color.GREEN) ), new Sized( Size.square(20), new Box(Color.BLUE) ) ) java new Stack( new SpriteWidget( new SpriteIdentifier( SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, Identifier.ofVanilla("block/lava_flow") // the lava flow sprite is 32x32, which is smaller than ) // the 64x64 braid logo ), new StackBase( // but by making the logo the base, the lava will be new BraidLogo() // force to have the same size, effectively using it ) // as a backdrop for the logo ) Code samples [​](https://docs.wispforest.io/braid/layout#code-samples) ----------------------------------------------------------------------- All code samples on this page can be found in [the repository](https://github.com/wisp-forest/owo-lib/tree/braid-ui/src/testmod/java/io/wispforest/owo/samples/braid/layout) . If you want to check them out in-game, launch the owo testmod, give yourself an `uwu:braid_samples` item, use it and open the "Layout Widget Examples". --- # Collapsible Container | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/collapsible-container#VPContent) Return to top Collapsible Container [​](https://docs.wispforest.io/owo/ui/components/collapsible-container#collapsible-container) ==================================================================================================================== `` `Containers.collapsible(...)` The collapsible container component allows you to create a collapsible section that can be expanded or collapsed. It supports a title text and various sizing and alignment options. **Parameters:** * `expanded`: Whether the collapsible container is initially expanded or not. * `text`: The title text of the collapsible container. * `padding`: The padding around the child components. * `surface`: The background surface of the collapsible container. * `horizontal-alignment`: The horizontal alignment of the child components. * `vertical-alignment`: The vertical alignment of the child components. * `allow-overflow`: Whether to allow child components to overflow the bounds of the collapsible container. **Example (Code-driven):** java Containers.collapsible(Sizing.content(), Sizing.content(), Text.literal("Collapsible Section"), true) .child(Components.label(Text.literal("Content"))) .padding(Insets.of(10)) .surface(Surface.PANEL) .horizontalAlignment(HorizontalAlignment.CENTER) .verticalAlignment(VerticalAlignment.CENTER) **Example (Data-driven):** xml Collapsible Section 10 center center --- # Dropdown | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/dropdown#VPContent) Return to top Dropdown [​](https://docs.wispforest.io/owo/ui/components/dropdown#dropdown) ============================================================================= `` `Components.dropdown(...)` The dropdown component creates a dropdown menu with various entries such as buttons, checkboxes, and nested dropdowns. It supports customizable entries and various sizing and alignment options. **Parameters:** * `close-when-not-hovered`: Whether to automatically close the dropdown when the mouse is not hovering over it. * `entries`: The entries of the dropdown menu. * `padding`: The padding around the dropdown entries. * `surface`: The background surface of the dropdown. * `horizontal-alignment`: The horizontal alignment of the dropdown entries. * `vertical-alignment`: The vertical alignment of the dropdown entries. * `allow-overflow`: Whether to allow the dropdown entries to overflow the bounds of the dropdown. **Example (Code-driven):** java Components.dropdown(Sizing.content()) .button(Text.literal("Option 1"), button -> { // Handle button click event }) .checkbox(Text.literal("Option 2"), false, ignored -> {}) .nested(Text.literal("Submenu"), Sizing.content(), submenu -> { submenu.button(Text.literal("Submenu Option"), button -> { // Handle submenu button click event }); }) .closeWhenNotHovered(false) .padding(Insets.of(5)) .surface(Surface.TOOLTIP) **Example (Data-driven):** xml false Option 2 false 5 --- # Flow Layout | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/flow-layout#VPContent) Return to top Flow Layout [​](https://docs.wispforest.io/owo/ui/components/flow-layout#flow-layout) ====================================================================================== `` `Containers.verticalFlow(...)` | `Containers.horizontalFlow(...)` The flow layout component arranges its children in a vertical or horizontal flow. It supports various alignment and sizing options. **Parameters:** * `direction` (required): The direction of the flow layout (`vertical`, `horizontal`, or `ltr-text-flow`). * `gap`: The gap between child components in pixels. * `padding`: The padding around the child components. * `surface`: The background surface of the flow layout. * `horizontal-alignment`: The horizontal alignment of the child components. * `vertical-alignment`: The vertical alignment of the child components. * `allow-overflow`: Whether to allow child components to overflow the bounds of the flow layout. **Example (Code-driven):** java Containers.verticalFlow(Sizing.fill(100), Sizing.content()) .child(Components.label(Text.literal("Item 1"))) .child(Components.label(Text.literal("Item 2"))) .gap(10) .padding(Insets.of(20)) .surface(Surface.PANEL) .horizontalAlignment(HorizontalAlignment.CENTER) .verticalAlignment(VerticalAlignment.CENTER) **Example (Data-driven):** xml 10 20 center center --- # owo-ui Components | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/#VPContent) Return to top owo-ui Components [​](https://docs.wispforest.io/owo/ui/components/#owo-ui-components) ======================================================================================= owo-ui provides a rich set of components that can be used to build interactive and customizable user interfaces. By leveraging these components and their various properties, you can create dynamic and visually appealing UIs using either the code-driven or data-driven approach. For more detailed information on each component and their usage, please refer to the individual component documentation. --- # Grid Layout | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/grid-layout#VPContent) Return to top Grid Layout [​](https://docs.wispforest.io/owo/ui/components/grid-layout#grid-layout) ====================================================================================== `` `Containers.grid(...)` The grid layout component arranges its children in a grid with a specified number of rows and columns. It supports various alignment and sizing options. **Parameters:** * `rows` (required): The number of rows in the grid. * `columns` (required): The number of columns in the grid. * `padding`: The padding around the child components. * `surface`: The background surface of the grid layout. * `horizontal-alignment`: The horizontal alignment of the child components within each cell. * `vertical-alignment`: The vertical alignment of the child components within each cell. * `allow-overflow`: Whether to allow child components to overflow the bounds of the grid layout. **Example (Code-driven):** java Containers.grid(Sizing.content(), Sizing.content(), 2, 2) .child(Components.label(Text.literal("Cell 1")), 0, 0) .child(Components.label(Text.literal("Cell 2")), 0, 1) .child(Components.label(Text.literal("Cell 3")), 1, 0) .child(Components.label(Text.literal("Cell 4")), 1, 1) .padding(Insets.of(10)) .surface(Surface.PANEL) .horizontalAlignment(HorizontalAlignment.CENTER) .verticalAlignment(VerticalAlignment.CENTER) **Example (Data-driven):** xml 10 center center --- # Label | Wisp Forest Docs [Skip to content](https://docs.wispforest.io/owo/ui/components/label#VPContent) Return to top Label [​](https://docs.wispforest.io/owo/ui/components/label#label) ==================================================================== `