Skip to content

Conversation

@eclipseisoffline
Copy link
Member

@eclipseisoffline eclipseisoffline commented Dec 4, 2024

If you are planning to use a build from this PR to create custom items making use of the 1.21.4+ format, please read the following:

To use the format described in this PR, you have to use a build from it. You can download the experimental preview builds here:

Again, this PR is experimental, and bugs will likely occur. You are free to report any either here or on the GeyserMC Discord, in the #custom-resource-packs channel. Breaking changes can still be made, but we try to keep these to a minimum!

Documentation for the new JSON mappings format is available below. Example mappings making use of this PR's format, as well as a bedrock resourcepack, are available here. If you have any questions regarding the format, please ask those in the aforementioned Discord channel.

Rainbow is a Fabric Minecraft mod capable of generating JSON mappings for this PR, as well as generating a bedrock resourcepack to go with those mappings.


This PR adds a new API for (vanilla and non-vanilla) custom items, along with a new JSON mappings format for it.

This new API was necessary to incorporate the amount of changes made recently to items in Minecraft Java, primarily the introduction of new item components and item model definitions, adding a new predicate system. The new API is designed to be somewhat similar to the component system and item model definitions.

The currently existing API and JSON mappings format will be deprecated, but will continue to be usable. Custom items made using the existing API will automatically be translated at runtime to the objects from the new API.

Along with introducing a new API for custom items, this PR also cleans up the custom item registry populator a bit and makes use of default item components to get the properties of vanilla items, and makes some other minor changes as well. These changes together should enhance the experience with custom items by a lot.


Before going into the specification, here is some vocabulary used in custom items on Java and Bedrock:

  • Java item: any real, existing item on vanilla Java. Java datapacks make custom items by overriding the components of a Java item.
  • Java item component: Java items have their properties and behaviour defined in item components. Every Java item has a set of default components, and item stacks can override these components.
  • Bedrock item component: Bedrock items also have components that determine item behaviour, similar to Java, however unlike Java items, these components can't be changed at runtime for item stacks, which is one of the major reasons a custom item API is required in the first place.
  • Java legacy custom model data: this system was used before Java 1.21.4. Before 1.21.4, each custom item had a custom model data number, used to decide which model a Java item should use.
  • Java item model definition: these were introduced in Java 1.21.4 and exist in the assets/<namespace>/items/ resource pack directory. They decide which model a Java item should use based on a set of rules and item properties. Every Java item stores its item model definition in its minecraft:item_model item component, which can in term be overridden on an item stack by a datapack to use a custom item model definition defined in a resource pack.
  • Custom item definition: a custom item definition is a Geyser term. It represents a single Bedrock custom item that is used to map (part of) a Java item model definition. It contains info on the item's properties on Java and Bedrock.
    • Multiple custom item definitions, and thus multiple Bedrock items, can exist for the same Java item model definition, but for every combination of a Java item and Java item model definition, there can only be one custom item definition without predicates.
  • Non-vanilla custom item definition: also a Geyser term. Represents a Bedrock custom item that maps a Java non-vanilla item, and is as such only used for modded items. Like normal custom item definitions, it contains info on the item's properties on Java and Bedrock.
    • At the moment, there can only be one non-vanilla custom item definition per Java non-vanilla item. In the future, this might be changed to add support for non-vanilla custom item definitions making use out of predicates.
    • Non-vanilla custom item definitions have some additional features on top of normal custom item definitions to allow configuring options that are not possible to configure for custom items made with Java vanilla items. These include options like chargeable (shooter) items.

The new format

The new format for JSON-mappings looks somewhat like this:

{
  "format_version": 2,
  "items": {
    "minecraft:flint": [
      {
        "type": "definition",
        "model": "geyser_mc:test_item",
        "bedrock_identifier": "geyser_mc:test_item",
        "display_name": "An Example Item!",
        "bedrock_options": {
          "icon": "potato",
          "creative_category": "items"
        }
      },
      {
        "type": "legacy",
        "custom_model_data": 42,
        "bedrock_identifier": "geyser_mc:test_legacy_item",
        "display_name": "A Very Old Item!",
        "bedrock_options": {
          "icon": "cobweb"
        }
      },
      {
        "type": "group",
        "model": "geyser_mc:another_test_item",
        "definitions": [
          {
            "bedrock_identifier": "geyser_mc:another_test_item",
            "bedrock_options": {
              "icon": "carrot"
            },
            "components": {
              "minecraft:consumable": {
                "animation": "drink",
                "consume_seconds": 10
              }
            }
          },
          {
            "bedrock_identifier": "geyser_mc:another_test_item_nether",
            "bedrock_options": {
              "icon": "carrot"
            },
            "predicate": {
              "type": "match",
              "property": "context_dimension",
              "value": "minecraft:the_nether"
            },
            "components": {
              "minecraft:consumable": {
                "animation": "drink",
                "consume_seconds": 10
              }
            }
          }
        ]
      }
    ]
  }
}

The start of the format is similar to the current format. There is an items key, which is an object in which keys are Java items, and in which each value is an array of objects, specifying custom item definitions for that Java item.

There are multiple types of custom item definitions:

  • definition: a single custom item, defined for a Java item model definition.
  • legacy: a single custom item, defined for a Java custom item making use of legacy custom model data numbers.
  • group: a group of custom item definitions.

The type of a definition is defined in the type key. All definition types will be explained in detail below.

Note: standard definitions (of type definition) are used by default, meaning that when no type key is present within a definition, Geyser assumes it's a standard definition.

Standard (normal) definitions and legacy definitions

Both the definition and legacy definition types are quite similar. They share a couple of keys, and only have one major difference.

The difference between standard and legacy definitions is the version of Minecraft they should be used for. As is said in the name, legacy definitions should be used for legacy custom items. Legacy in this context means anything making use of custom model data numbers below Java 1.21.4. For all custom items created for Java 1.21.4 or later, standard definitions should be used.

Both definition types expect a bedrock_identifier key. This determines its identifier on Bedrock, which is also used in e.g. Bedrock attachables. If no namespace is given here, the geyser_custom namespace is used. Note that every custom item definition, across mapping files too, is required to have a unique Bedrock identifier.

Standard definitions expect a model key, noting the value of the minecraft:item_model component for this custom item. Legacy definitions however, expect a custom_model_data key, an integer describing the custom model number of this custom item.

These two keys, along with the type key, are the only required keys for standard and legacy definitions. There are however a few additional keys that can be used to further customise the custom item definition, which are:

  • bedrock_options
  • components
  • predicate
  • predicate_strategy
  • priority
  • display_name

These will be explained in detail further below.

Before going to group definitions, here's how a standard definition looks like in JSON:

{
  "type": "definition",
  "model": "geyser_mc:a_cool_item_model",
  "bedrock_identifier": "geyser_mc:a_cool_item"
}

And the same for a legacy one:

{
  "type": "legacy",
  "custom_model_data": 42,
  "bedrock_identifier": "geyser_mc:a_cool_item"
}

Group definitions

A group definition is, well, a group of custom item definitions. These can both be standard or legacy definitions. All standard definitions in the group inherit the Java model definition of the group, if any (so, a group is not required to have a model, in which case the definitions in the group have to specify the model themselves). Definitions in a group are also allowed to be a group, and definitions in a group can also override the Java model definition of the group.

A group definition can look something like this:

{
  "type": "group",
  "model": "geyser_mc:a_cool_item_model", // Optional
  "definitions": [...]
}

Custom item definition bedrock options

The bedrock_options key is an object that sets options for the item on Bedrock, that cannot be set using Java item components. Possible keys are:

  • icon: determines the icon to use for the item. If not set, the item's bedrock identifier is used, : replaced with . and / with _ (for example, geyser_mc:a_cool_item turns into geyser_mc.a_cool_item).
  • allow_offhand: if the item should be allowed in the offhand slot. Defaults to true.
  • display_handheld: if the item should display handheld, like a tool or weapon. Defaults to false.
  • protection_value: determines how many armour points should be shown when this item is worn. This is purely visual. Only has an effect when the item is equippable, and defaults to 0.
  • creative_category: sets the item's creative category (for the recipe book). Can be none, construction, nature, equipment, or items. Defaults to none.
  • creative_group: sets the item's creative group (for the recipe book). See this page for possible values.
  • tags: Bedrock tags the item has, can be used in Molang.

The render_offsets and texture_size options from v1 have been removed. render_offsets has been deprecated for a long time, and is longer functional on the bedrock client, and texture_size depended on it. As an alternative, use attachables in your bedrock resourcepack instead.


Custom item definition components

The components key defines properties for the item, in the Java item component format. It is expected that the item will always have these components when the custom item definition is used. Currently, the following components are supported:

  • minecraft:consumable: doesn't support consume particles/sounds.
  • minecraft:equippable: doesn't support the camera overlay or swappable properties.
  • minecraft:food
  • minecraft:max_damage
  • minecraft:max_stack_size
  • minecraft:use_cooldown
  • minecraft:enchantable
    • On Bedrock, this will be mapped to the minecraft:enchantable component with slot=all. This should, but does not guarantee, allow for compatibility with vanilla enchantments. Non-vanilla enchantments are unlikely to work.
  • minecraft:tool
  • minecraft:repairable
  • minecraft:enchantment_glint_override

Some components, like minecraft:rarity and minecraft:attribute_modifiers, are already automatically translated and don't need to be specified here.

Along with the components listed here, default component removals can also be specified by prefixing a component with a !, like is done in Java:

"components": {
  "!minecraft:consumable": {}
}

Custom item definition predicates

predicate is either an object (single predicate) or an array of objects (multiple predicates). For each combination of a Java item and a Java item model definition, there can be one item definition without a predicate, and one or multiple definitions with a predicate. There can't be multiple item definitions with the same predicates for the same Java item and Java item model definition. If the Java item model definition is in the minecraft namespace, there can't be an item definition without a predicate.

Each predicate has a type and a property key. Currently, there are 3 predicate types:

  • condition
  • match
  • range_dispatch

The condition predicate type checks for a boolean property and returns true if the property matches the expected value. It has 4 possible properties:

  • broken: if the item is broken (has only 1 durability point left).
  • damaged: if the item is damaged (not at full durability).
  • custom_model_data: checks the item's custom model data flags. Defaults to false.
  • has_component: if the item has the component set in the component key. Includes default components.
  • fishing_rod_cast: if the player is currently holding a cast fishing rod.

The condition predicate also has the expected key, which specifies if the property has to be true or false for this predicate to be true. Defaults to true.

The match predicate type checks for a text-like property and returns true if it matches the given value. It has 4 possible properties:

  • charge_type: the item currently charged in the crossbow (in the minecraft:charged_projectiles component). Can be none, arrow, or rocket.
  • trim_material: the trim material (resource location) of this item.
  • context_dimension: the dimension (resource location) the player is currently in.
  • custom_model_data: fetches a string from the item's custom model data strings.

The match predicate requires a value to be specified in the value key.

The range_dispatch predicate type checks for a numeric property and returns true if it is above the specified threshold. It has 4 possible properties:

  • bundle_fullness: checks the item's bundle fullness. Returns the total stack count of all the items in a bundle (in the minecraft:bundle_contents component).
  • damage: checks the item's damage value. Can be normalised.
  • count: checks the item's count. Can be normalised.
  • custom_model_data: checks the item's custom model data floats. Defaults to 0.0.

The range_dispatch predicate has 3 extra keys, one of them required:

  • threshold: the threshold required to return true (required).
  • scale: the factor to scale the property value by before comparing it with the threshold. Defaults to 1.0.
  • normalize: if the property value should be normalized before scaling it and comparing it with the threshold. Defaults to false, only works for certain properties.

All predicates can also have an index key, which determines which index to use when using a custom model data property. Defaults to 0.

Some may notice these predicates are similar to the ones possible in Java 1.21.4's item model definitions. Here are some examples of how predicates can look:

{
  "type": "match",
  "property": "charge_type",
  "value": "arrow"
}
{
  "type": "condition",
  "property": "custom_model_data",
  "index": 1
}
{
  "type": "match",
  "property": "context_dimension",
  "value": "minecraft:overworld"
}
{
  "type": "condition",
  "property": "has_component",
  "component": "minecraft:unbreakable"
}

There is also a predicate_strategy key, which can be and or or and defaults to and. This was inspired by the advancement requirement strategies, and decides if all predicates (and), or only one predicate (or) of an item definition has to match for it to be used.


Custom item definition sorting and priority

Custom item definitions are automatically sorted so that the definitions' predicates are checked in a correct order when translating items. Specifically, Geyser sorts custom item definitions like this:

  1. First custom item definitions with a higher priority value are sorted above definitions with lower ones.
  2. Custom item definitions with similar range dispatch predicates are sorted by their threshold, with higher thresholds going first.
  3. Custom item definitions with more predicates are sorted over definitions with less.

This system ensures that in most cases, item definitions with predicates are checked in proper order, no matter which order they are put in the mappings. In the few cases that Geyser does not sort definitions correctly (that will most likely occur when using multiple range_dispatch predicates, or using them in combination with other predicates), the priority key can be used to specify which definitions should be checked first.


The API and non-vanilla custom items

Along with the introduction of a new JSON mappings format, the entire custom item API has received an overhaul too. Detailed changes won't be described here, but there are Javadocs documenting all the functionality pretty well. Just as the v1 JSON mappings format will continue to be usable, the old custom item API can also continue to be used for the time being.

You can experiment with the new API for extensions by including a JitPack build of this branch of the Geyser API, instead of the main build. You can do so as follows in your build.gradle:

repositories {
    maven {
        url = "https://jitpack.io"
    }
}

dependencies {
    implementation 'com.github.eclipseisoffline:geyser:custom-item-api-v2-SNAPSHOT'
}

Javadocs for the new API are available here. The classes in the org.geysermc.geyser.api.item.custom.v2 package are the important ones.


TODOs

A lot of the TODOs for this PR are now finished. There are still some left on this list and in the code, but none are of a very significant magnitude.

  • Range dispatch predicate
  • Non vanilla custom items
  • Possibly main hand match property not possible
  • Possibly using item, has component cast fishing rod, selected bundle item condition properties
    • Only has component and cast fishing rod were possible.
  • Consumable component properties (sound, consume particles, etc.) if those are possible
    • Might be server side, check Tested, consume particles are done entirely client side, and the sound is only sent to clients other than the client consuming the item. Doesn't seem possible to emulate this to me.
  • Fix consumable animations/, use animation/use duration properties on bedrock which seem to be randomly used with magic numbers
  • Fix protection value/enchantment value properties
  • Check and possibly fix Bedrock tool, chargeable, throwable and possibly other components that may have changed and not properly updated in the custom item registry populator
  • Check what to do with the minecraft:tool Java component
  • Make sure stack size is 1 when an equippable is set since Bedrock doesn't support armour with a stack size above 1, also validate other components (e.g. damage and stack size combination)
  • Add a custom model data predicate when converting v1 to v2 at runtime (when range dispatch predicate is added)
  • Unbreakable condition predicate, also implement in v1 to v2 converter
  • Custom item definition priority
  • Don't use MCPL in API module, when creating component classes in API make sure to do proper validation
  • Cleanup item registry populator: registeredItemNames may be identifiers, identifierToKey method needs to go elsewhere, customItemName is always the bedrock identifier
  • Don't use adventure in API module
  • More proper PR description of code changes
  • Block mapping reading in the V2 reader
  • Better error handling/printing when mapping reading/custom item registry populator fails
  • Clean up mapping reader/data component JSON reader with helper methods to lessen code duplication
  • Fix the broken/damaged conditional predicates
  • Take range dispatch scaling into account when sorting predicates
  • Map food nutrition/saturation properties, if bedrock sends them to the client
  • Update PR description for range dispatch predicates, unbreakable condition predicate and priority
  • Maybe predicate caching, so that predicates that occur in multiple definitions for the same item/item model aren't recalculated multiple times
  • Move predicate stuff in API to core
  • Replace unbreakable predicate with has unbreakable component once has component predicate is a thing
  • Predicates AND/OR strategy
  • Predicate validation: check if indices are non negative
  • Predicate/bedrock options node reader?
  • Custom model data arrows/projectiles (by defining projectile component, also make this an option for non-vanilla items)
  • Some components need fixing (block placer, chargeable (animation))
  • Predicate abstraction: eclipseisoffline/Geyser#2
  • Documentation
  • Other TODOs left in the code
  • A lot of testing, probably

Although the PR is not finished yet, the code is decently clean, and (especially the API module), somewhat well documented. Reviews are welcome!


Testing

Some testing has been done already with a lot of (relatively simple) custom items that make use out of components like consumable, equippable and use_cooldown and use the condition and match predicates. This seemed to work decent so far. Testing that still has to be done:

  • Verify that v1 mappings convert over correctly and can be used without much downsides
  • Range dispatch predicates
  • Creating custom item definitions that for a minecraft: (vanilla) item model with a predicate
  • Error handling
  • Predicate strategies
  • Anything you can think of that should work with this new system

@Kas-tle
Copy link
Member

Kas-tle commented Dec 5, 2024

FYI I believe that the plan for items on Bedrock itself is to eventually deprecate render offsets on items in favor of attachables/texture meshes, so we should probably note that in any documentation.

Also as long as you're adding stuff, it might be nice to add some sort of a priority value at the top level of the mappings file for specifying the load order of files since I've seen a lot of cases where people want to separate the same item across files for organizational reasons, but cannot because it results in predicates being applied incorrectly.

Otherwise I think the plan for the format is solid. Haven't reviewed the code yet since it sounds like that's still a WIP.

@eclipseisoffline
Copy link
Member Author

I had a feeling that might be the case - maybe we should just mark the whole render offsets option as deprecated all together, telling users to use attachables/texture meshes instead?

As for priority values, I've been working on automatically sorting predicates. For example, let's say you have 3 item definitions for the same item model:

- one with predicate A
- one with predicates A and B (A && B)
- one with predicates A and C (A && C)
- one without predicates (fallback)

Geyser would then automatically sort them from most predicates to least, so that it'll match them in this order:
- A && B
- A && C
- A
- Fallback, if no predicates match

This way no matter the order you put predicates in the mappings, they'd always be checked in the right order so that the A predicate won't be used if A && C also matches.

This worked pretty well until I got to range dispatch predicates - I made these sort by comparing their threshold value, and predicates with higher threshold values will be checked first. This doesn't always work properly however, for example when you have a definition with multiple range dispatch predicates checking for different properties with different thresholds.

It might be a good idea to add an optional priority key to item definitions themself, for example like this:

{
  "type": "group",
  "model": "geyser_mc:test_item",
  "definitions": [
    {
      "bedrock_identifier": "geyser_mc:test_item_1",
      "priority": 1,
      "predicate": [
        {
          "type": "range_dispatch",
          "property": "custom_model_data",
          "threshold": 100.0,
          "index": 0
        },
        {
          "type": "range_dispatch",
          "property": "custom_model_data",
          "threshold": 50.0,
          "index": 1
        }
      ]
    },
    {
      "bedrock_identifier": "geyser_mc:test_item_2",
      "priority": 2,
      "predicate": [
        {
          "type": "range_dispatch",
          "property": "custom_model_data",
          "threshold": 20.0,
          "index": 1
        },
        {
          "type": "range_dispatch",
          "property": "custom_model_data",
          "threshold": 30.0,
          "index": 2
        }
      ]
    }
  ]
}

This way the second custom item definition would be checked first.

And yeah - code is definitely still WIP.

# Conflicts:
#	core/src/main/java/org/geysermc/geyser/item/type/Item.java
#	core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java
#	core/src/main/java/org/geysermc/geyser/translator/item/CustomItemTranslator.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java
@onebeastchris onebeastchris added the PR: Needs Testing When a PR needs testing but is currently not under review label Jul 17, 2025
*
* @return the item's base data components and the "additional" ones that may exist.
*/
public @Nullable DataComponents getAllComponents() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How practical/reasonable would it be to remove the componentCache field and replace this with a getAllComponents(GeyserSession) call? Having all items defined with a session parameter still rubs me the wrong way. Defining EMPTY above with a null session solidifies that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would also require getComponent() to also require passing a GeyserSession.. 40 usages there alone. Bigger issue would be that helper methods, such as e.g. GeyserItemStack#isDamageable would then also require passing GeyserSession everywhere - and so would every other method checking item components.

fwiw the session is not stored directly either, the ComponentCache field is - passing around ComponentCache there would be a bit iffy as that would require each caller null-checking the session before getting the componentcache
image

@almecho
Copy link

almecho commented Sep 11, 2025

any chance of getting an updated build for this with the changes in master?

@eclipseisoffline
Copy link
Member Author

The next build will have master's latest changes.

# Conflicts:
#	core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java
#	core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockBlockActions.java
…line/custom-item-api-v2

# Conflicts:
#	api/src/main/java/org/geysermc/geyser/api/util/Identifier.java
#	core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEggEntity.java
#	core/src/main/java/org/geysermc/geyser/impl/IdentifierImpl.java
#	core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java
#	core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java
#	core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java
#	core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
@suerion
Copy link

suerion commented Oct 13, 2025

How far are the tests? Would it be added soon in the master? Hopefully soon

@SleepyPopp
Copy link
Contributor

How far are the tests? Would it be added soon in the master? Hopefully soon

Eventually, it will be. Just no ETAs lol.

@Jsaunders411
Copy link

Does this get updated with the latest 1.21.10 and support for copper age? Just curious

@onebeastchris
Copy link
Member

it's already been updated

@GeyserMC GeyserMC locked and limited conversation to collaborators Oct 14, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

API The issue/feature request relates to the Geyser API PR: Feature When a PR implements a new feature PR: Needs Testing When a PR needs testing but is currently not under review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom Item components such as "item_model" and "asset_id" from a datapack do not work on bedrock/geyser