Skip to main content

Entry Types

RegistryLib provides specialized Entry types that wrap DeferredHolder with convenience helpers. All entry types extend RegistryEntry<R, T> and are lazily resolved —the underlying object is created only when NeoForge's registration event fires.

note

All entry types also implement Holder-style interfaces. When an API expects a Holder<Item>, Holder<Block>, or Holder<Fluid>, you can pass the entry directly.

Inheritance Hierarchy

The entry type class hierarchy is:

RegistryEntry<R, T>
├── AbstractHolderEntry<R, T> (implements Holder<R>)
│ ├── ItemProviderEntry<T, S> (implements ItemLike)
│ │ ├── ItemEntry<T>
│ │ └── BlockEntry<T>
│ └── FluidEntry<T>
├── EntityEntry<T>
├── BlockEntityTypeEntry<T>
├── DataComponentTypeEntry<T>
└── AttachmentTypeEntry<T>

AbstractHolderEntry<R, T> is the base class for entry types that act as Holder<R> delegates. It implements all Holder methods by delegating to the underlying builtInRegistryHolder obtained from the registered object. Subclasses only need to implement delegate() to return that holder.

Common RegistryEntry Methods

All RegistryEntry<R, T> subclasses inherit these methods:

MethodReturn typeDescription
get()TGet the registered object. Throws IllegalStateException if the entry has not been bound yet (i.e. accessed before registration completes).
getOptional()Optional<T>Get the registered object wrapped in an Optional. Returns Optional.empty() if the entry has not been bound yet. Safe to call at any time.
isBound()booleanReturns true if the entry has been bound to a registered object. Use this to check whether get() is safe to call.
key()ResourceKey<R>The registry key for this entry
identifier()IdentifierThe resource identifier for this entry
warning

get() throws IllegalStateException when called before registration is complete. If you need to access an entry's value during early initialization or in a context where registration may not have finished, use getOptional() or check isBound() first.

note

ChunkStateEntry, WorldStateEntry, and WorldgenFeatureEntry are boundary handles, not RegistryEntry subclasses. They expose the ids and runtime access methods needed for their domain without pretending to be normal registry objects.

ItemEntry<T>

Extends: AbstractHolderEntry<Item, T> (via ItemProviderEntry)

MethodReturn typeDescription
get()TGet the registered Item instance (throws IllegalStateException if unbound)
asStack()ItemStackCreate a default ItemStack (count 1)
asStack(int count)ItemStackCreate an ItemStack with the specified count
readOnlyStack()ItemStackDefensive copy of a cached ItemStack (count 1); safe against external mutation
asResource()ItemResourceItemResource wrapper for transfer APIs

Usage example:

ItemEntry<Item> COPPER_COIN = REGISTRYLIB.item("copper_coin", Item::new)
.lang("Copper Coin")
.register();

// Later in code
ItemStack stack = COPPER_COIN.asStack(16);
Item item = COPPER_COIN.get();

BlockEntry<T>

Extends: AbstractHolderEntry<Block, T> (via ItemProviderEntry)

MethodReturn typeDescription
get()TGet the registered Block instance (throws IllegalStateException if unbound)
getDefaultState()BlockStateBlock's default BlockState for placement or configuration

Usage example:

BlockEntry<Block> DECORATIVE_STONE = REGISTRYLIB.block("decorative_stone", Block::new)
.initialProperties(() -> Blocks.STONE)
.simpleItem()
.register();

// Later in code
BlockState state = DECORATIVE_STONE.getDefaultState();

FluidEntry<T>

Extends: AbstractHolderEntry<Fluid, T>

The most feature-rich entry type. Provides access to the entire fluid family (source, flowing, block, bucket) from a single reference.

MethodReturn typeDescription
get()TGet the flowing fluid (throws IllegalStateException if unbound)
getSource()FluidGet the source fluid
getType()FluidTypeGet the FluidType
getBlock()LiquidBlockGet the fluid block (if registered)
getBucket()ItemGet the bucket item (if registered)
asStack()FluidStackCreate a FluidStack (1000 mB)
asStack(long amount)FluidStackCreate a FluidStack with the specified amount
readOnlyStack()FluidStackCached read-only FluidStack (1000 mB); avoids repeated allocations
asResource()FluidResourceFluidResource wrapper for transfer APIs
warning

getBlock() and getBucket() return null if the fluid was registered without .block(...) or .bucket(...) in its builder chain.

Usage example:

FluidEntry<BaseFlowingFluid.Flowing> MOLTEN_GOLD = REGISTRYLIB
.fluid("molten_gold", STILL_TEXTURE, FLOW_TEXTURE)
.lang("Molten Gold")
.block(b -> {})
.bucket(b -> {})
.register();

// Later in code
FluidStack stack = MOLTEN_GOLD.asStack(500);
Fluid source = MOLTEN_GOLD.getSource();
Item bucket = MOLTEN_GOLD.getBucket();

BlockEntityTypeEntry<T>

Extends: RegistryEntry<BlockEntityType<?>, BlockEntityType<T>>

MethodReturn typeDescription
get()BlockEntityType<T>Get the BlockEntityType (throws IllegalStateException if unbound)

Host block binding is configured during registration via validBlock() or validBlocks() on the builder:

BlockEntityTypeEntry<MyBlockEntity> MY_BE = REGISTRYLIB
.blockEntity("my_be", MyBlockEntity::new)
.validBlock(MY_BLOCK)
.renderer(() -> () -> MyBlockEntityRenderer::new)
.register();
tip

Bind multiple blocks with validBlocks(block1, block2, ...) when the same BlockEntity type is shared across several blocks.

DataComponentTypeEntry<T>

Extends: RegistryEntry<DataComponentType<?>, DataComponentType<T>>

MethodReturn typeDescription
get()DataComponentType<T>Get the registered component type (throws IllegalStateException if unbound)
get(stack)TRead a component value from an ItemStack
getOrDefault(stack, defaultValue)TRead a component value with a fallback
has(stack)booleanCheck whether the stack has the component
set(stack, value)TSet the component value and return the previous value
remove(stack)TRemove the component and return the previous value
component(properties, value)Item.PropertiesAdd the component to item properties
DataComponentTypeEntry<String> API_NOTE = REGISTRYLIB.dataComponentTypeEntry(
"api_note",
builder -> builder.persistent(Codec.STRING));

String note = API_NOTE.getOrDefault(stack, "");
API_NOTE.set(stack, "Stored on the stack");

AttachmentTypeEntry<T>

Extends: RegistryEntry<AttachmentType<?>, AttachmentType<T>>

MethodReturn typeDescription
get()AttachmentType<T>Get the registered NeoForge attachment type (throws IllegalStateException if unbound)
getOrCreate(holder)TRead or create data on an IAttachmentHolder
getIfPresent(holder)Optional<T>Read existing data without creating it
set(holder, value)TReplace attachment data and return the previous value
remove(holder)TRemove attachment data and return the previous value
sync(holder)voidSync attachment data through NeoForge

Prefer chunkState(...) or worldState(...) when the attachment represents gameplay state.

ChunkStateEntry<T>

Does not extend: RegistryEntry

MethodReturn typeDescription
identifier()IdentifierLogical state id
scope()StateScopeAlways CHUNK
codec()Codec<T>Persistent value codec
attachmentType()AttachmentType<T>Underlying attachment type
debugConfig()StateDebugConfigDebug command configuration
getOrCreate(level, chunkPos)TRead or create chunk state
getIfLoaded(level, chunkPos)Optional<T>Read without force-loading the chunk
set(level, chunkPos, value)voidReplace the value and mark the chunk unsaved
modify(level, chunkPos, action)voidMutate the value and mark the chunk unsaved
sync(level, chunkPos)voidManually sync the state

WorldStateEntry<T>

Does not extend: RegistryEntry

MethodReturn typeDescription
identifier()IdentifierLogical state id
scope()StateScopeAlways WORLD
codec()Codec<T>Persistent value codec
attachmentType()AttachmentType<T>Underlying attachment type
debugConfig()StateDebugConfigDebug command configuration
getOrCreate(level)TRead or create world state
getIfPresent(level)Optional<T>Read without creating state
set(level, value)voidReplace the value
modify(level, action)voidMutate the value
sync(level)voidManually sync the state

WorldgenFeatureEntry

Record fields: configuredKey, placedKey

MethodReturn typeDescription
configuredKey()ResourceKey<ConfiguredFeature<?, ?>>Key for the generated configured feature
placedKey()ResourceKey<PlacedFeature>Key for the generated placed feature

EntityEntry<T>

Extends: RegistryEntry<EntityType<?>, EntityType<T>>

MethodReturn typeDescription
get()EntityType<T>Get the EntityType (throws IllegalStateException if unbound)
is(entity)booleanCheck if an Entity instance is of this type

Usage example:

EntityEntry<CrystalGuardian> CRYSTAL_GUARDIAN = REGISTRYLIB
.<CrystalGuardian>entity("crystal_guardian", CrystalGuardian::new, MobCategory.MONSTER)
.attributes(CrystalGuardian::createAttributes)
.renderer(() -> () -> CrystalGuardianRenderer::new)
.register();

// Later in code
EntityType<CrystalGuardian> type = CRYSTAL_GUARDIAN.get();
boolean match = CRYSTAL_GUARDIAN.is(someEntity);
tip

Entities with attributes (any LivingEntity subclass) must call .attributes() on the builder. Omitting it causes a crash at entity spawn time.

See Also