Minecraft Adapter APIs
Use stable Nows adapter helpers for registries, text, NBT, keybinds, recipe displays, events and generated data.
Minecraft Adapter APIs
mc/<version> exposes Minecraft-facing API helpers for the selected game version. In 0.6.0 the stable path is Nows-owned input values first, then adapter translation into the real Minecraft API for that version.
Mods can use the same Nows API class names across supported versions while each adapter handles common version details:
RegistryApi registries = MinecraftApi.registries(context);
TextApi text = MinecraftApi.text(context);
NbtApi nbt = MinecraftApi.nbt(context);
Item item = registries.registerItem(ItemSpec.builder("my_mod:widget")
.maxStackSize(16)
.build());
BlockEntry block = registries.registerBlockWithItem(
BlockSpec.builder("my_mod:machine")
.material(BlockMaterial.METAL)
.strength(2.0F, 6.0F)
.requiresCorrectTool()
.item(ItemSpec.builder("my_mod:machine").maxStackSize(32).build())
.build());
ItemStack stone = registries.itemStack(ItemStackSpec.of("minecraft:stone", 2));
The older native escape hatches still exist for advanced code, such as custom item/block factories and raw registry registration. Prefer the stable specs when the mod only needs common item, block, stack or material intent.
The adapter entrypoint is MinecraftApi. It resolves services from NowsContext, so use it after the loader has created the mod context, usually inside your mod entrypoint or a lifecycle listener that runs after bootstrap.
What The Adapter Owns
| Area | Current helper shape |
|---|---|
| registry ids, keys and tags | id, resourceLocation, key, tag, itemKey, blockKey, itemTag, blockTag |
| basic items and blocks | plain item/block registration, configurable properties, custom factories |
| content registration | food, sword, armor, block item, block with item, menus, block entities, recipes, sounds, creative tabs |
| text | literal, translatable and keybind components |
| NBT | compound/list construction, common primitive reads/writes, explicit fallbacks |
| client | UI hooks, config screens, keybinds and local player helpers |
| data and events | data packs, generated JSON, command registration and tick callbacks |
The returned objects are still Minecraft objects for the target adapter. That is deliberate: a mod can use Nows for stable setup and then continue with normal Minecraft APIs.
Content Mods
For content-heavy mods, the registry adapter also covers the common objects that usually differ between Fabric, Forge and Minecraft versions:
RecipeType<MyRecipe> type = registries.registerRecipeType("my_mod:oven_baking");
RecipeSerializer<MyRecipe> serializer = registries.registerRecipeSerializer(
"my_mod:oven_baking",
MyRecipeSerializer.create());
MenuType<MyMenu> menu = registries.registerMenu("my_mod:oven", MyMenu::new);
BlockEntityType<MyBlockEntity> blockEntity = registries.registerBlockEntity(
"my_mod:oven",
MyBlockEntity::new,
ovenBlock);
SoundEvent sound = registries.registerVariableRangeSound("my_mod:frying");
These helpers are aimed at the boring registration layer that changes shape across loaders and Minecraft versions:
registerRecipeTypecreates the named recipe type and gives it a stable string id for recipe lookup and debugging.registerRecipeSerializerregisters your custom serializer object without making the mod repeat the raw registry call in every target.registerMenuregisters a simple two-argumentAbstractContainerMenufactory, the common pattern for inventory/container menus.registerBlockEntityregisters a block entity type from a NowsBlockEntityFactory, so mods do not need to depend on Minecraft’s version-specific builder or supplier names.registerVariableRangeSoundregisters a regular variable-rangeSoundEvent, matching the usual custom sound event pattern.
Item And Food Mods
For item-only or food-heavy mods, keep the mod data in your own constants and call the item helpers in a loop. This is the easiest path for mods that mainly differ by food values, stack sizes, creative tabs or generated assets:
Map<String, Item> foods = new LinkedHashMap<>();
for (FoodEntry entry : FoodEntries.ALL) {
Item item = registries.registerFood(
"my_mod:" + entry.id(),
new FoodProperties.Builder()
.nutrition(entry.nutrition())
.saturationModifier(entry.saturation())
.build(),
props -> props.stacksTo(entry.maxStackSize()));
foods.put(entry.id(), item);
}
registries.registerCreativeTab(
"my_mod:foods",
text.translatable("itemGroup.my_mod.foods"),
() -> new ItemStack(foods.get("complete_breakfast")),
(parameters, output) -> foods.values().forEach(output::accept));
Machines And Workstations
For machine or workstation mods, keep the machine behavior in normal Minecraft subclasses and use Nows only for the stable registration boundary. A cooking block entity can still own its inventory, ticking, recipe lookup, save/load and experience logic directly. Nows only removes the repeated registry wiring:
Block ovenBlock = registries.registerCustomBlock("my_mod:oven",
props -> new OvenBlock(props.strength(3.5F)));
BlockItem ovenItem = registries.registerBlockItem("my_mod:oven", ovenBlock);
RecipeType<OvenRecipe> ovenRecipes = registries.registerRecipeType("my_mod:oven");
RecipeSerializer<OvenRecipe> ovenSerializer = registries.registerRecipeSerializer(
"my_mod:oven",
OvenRecipeSerializer.create());
BlockEntityType<OvenBlockEntity> ovenEntity = registries.registerBlockEntity(
"my_mod:oven",
OvenBlockEntity::new,
ovenBlock);
MenuType<OvenMenu> ovenMenu = registries.registerMenu("my_mod:oven", OvenMenu::new);
Mods with simple facing machine blocks can reuse Nows base block classes instead of copying the same state boilerplate across versions:
import space.nows.mcnows.mc.api.registry.block.HorizontalBlock;
import space.nows.mcnows.mc.api.registry.block.HorizontalLitBlock;
Block pan = registries.registerCustomBlock("my_mod:pan",
props -> new HorizontalBlock(props.strength(1.5F)));
Block stove = registries.registerCustomBlock("my_mod:stove",
props -> new HorizontalLitBlock(props.strength(3.5F).lightLevel(state ->
state.getValue(HorizontalLitBlock.LIT) ? 13 : 0)));
HorizontalBlock adds the HORIZONTAL_FACING property, placement direction, rotation and mirror behavior. HorizontalLitBlock adds the same facing behavior plus Minecraft’s LIT property. They are useful for simple props, stoves, pans, ovens and other workstation blocks. If a block has custom voxel shapes, waterlogging, redstone behavior, menu opening, ticking or entity interaction, subclass one of these or use registerCustomBlock with your own Minecraft block class.
Lookup And Simple Logic
Existing registry entries can be queried with Optional-returning methods or fail-fast getters:
var widgetId = registries.resourceLocation("my_mod:widget");
ResourceKey<Item> widgetKey = registries.itemKey("my_mod:widget");
TagKey<Block> machineBlocks = registries.blockTag("my_mod:machines");
registries.item("minecraft:diamond").ifPresent(stackItem -> {});
Item diamond = registries.getItem("minecraft:diamond");
Block stone = registries.getBlock("minecraft:stone");
Use registries.id(...) or registries.resourceLocation(...) for the selected adapter’s real resource-id type. Newer adapters return Identifier; older adapters return ResourceLocation. registries.key(...), itemKey(...), blockKey(...), tag(...), itemTag(...) and blockTag(...) cover the common registry key/tag cases when the target Minecraft version exposes those classes.
Text helpers cover the common literal, translation and keybind component constructors without making mods remember when Minecraft renamed those factories:
Component title = text.component(McText.translatable("screen.my_mod.settings"));
Component help = text.component(McText.keybind("key.my_mod.open_oven"));
McText is the stable public value. Each adapter maps it to TextComponent, Component.literal, Component.translatable or the matching Minecraft text API for that version.
Keybinds
Client mods can register keybind categories and key mappings through Nows instead of wiring each Minecraft version by hand:
KeybindApi keys = MinecraftApi.keybinds(context);
keys.registerCategory("key.categories.my_mod");
keys.registerKeyboard(
"key.my_mod.open_oven",
"key.categories.my_mod",
GLFW.GLFW_KEY_O,
() -> MyScreens.openOven(context));
For actions that should run while the key is held, keep the returned mapping and use the normal client tick hook:
KeybindRegistration boost = keys.registerKeyboard(
"key.my_mod.boost",
"key.categories.my_mod",
GLFW.GLFW_KEY_B);
MinecraftApi.events(context).clientTick(client -> {
if (boost.isDown() && client.player != null) {
client.player.setDeltaMovement(client.player.getDeltaMovement().multiply(1.2D, 1.0D, 1.2D));
}
});
The default category is key.categories.nows. Key ids and categories should be translation keys so Minecraft’s Controls screen can show localized names.
registerKeyboard(id, category, key, onPress) installs a press callback directly. If the action depends on continuous state, keep the returned KeybindRegistration and poll isDown() in GameEvents.clientTick.
NBT helpers cover common compound/list reads and writes with explicit fallbacks. 0.6.0 also adds stable Nows NBT payloads that adapters convert to the real Minecraft tag classes:
NbtCompound data = nbt.stableCompound()
.putString("owner", "my_mod")
.putInt("heat", 7)
.putBoolean("active", true);
CompoundTag nativeData = nbt.compound(data);
Tag nativeValue = nbt.tag(NbtValue.compound(data));
The native helpers remain available for direct reads/writes on Minecraft CompoundTag and ListTag. Item-stack custom data is still deliberately separate because Minecraft 1.20.5+ moved much of that surface toward data components.
Use direct Minecraft APIs for anything outside this list: world saves, block entity serialization details, data components, custom renderers, entity behavior and low-level packet encoding.
Recipe Viewer Porting
Mods that already expose recipes to JEI can move their category and layout logic to Nows without depending on JEI at compile time. The RecipeViewerApi stores neutral display metadata that can be bridged later to JEI, REI, EMI or a built-in viewer:
RecipeViewerApi recipes = MinecraftApi.recipeViewer(context);
recipes.registerCategory(
"my_mod:oven",
OvenRecipe.class,
text.translatable("recipe.my_mod.oven"),
new ItemStack(ovenBlock),
(recipe, layout) -> layout
.input(20, 18, recipe.ingredient())
.catalyst(20, 52, new ItemStack(ovenBlock))
.output(82, 18, recipe.result())
.build());
recipes.registerCatalyst("my_mod:oven", new ItemStack(ovenBlock));
recipes.registerRecipeTransfer("my_mod:oven", ovenMenu);
The porting shape mirrors the common JEI split:
registerCategoryreplaces the JEI category registration boundary while keeping the mod’s own recipe class.RecipeViewerLayout.Buildercovers the usualinput,inputStack,inputStacks,output,outputStacksandcatalystslots with stable x/y coordinates.registerCatalystrecords workstation or tool stacks that should open the category.registerRecipeTransferlinks a menu type to the category so a later viewer bridge can offer transfer buttons.
Keep recipe lookup, validation and screen rendering in normal Minecraft code. This API is only the shared display contract, so the same mod code can run on Nows even when JEI is absent.
For simple behavior, mods can attach small logic hooks:
registries.registerItem("my_mod:wrench", props -> props.stacksTo(1), new ItemLogic() {
@Override
public InteractionResult useOn(UseOnContext context) {
return InteractionResult.SUCCESS;
}
});
registries.registerBlock("my_mod:speed_plate", props -> props.strength(1.0F), new BlockLogic() {
@Override
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
entity.setDeltaMovement(entity.getDeltaMovement().multiply(1.4D, 1.0D, 1.4D));
}
});
For custom behavior beyond the basic layer, use Minecraft’s API directly. registerCustomItem and registerCustomBlock accept your own Item/Block subclasses.