Minecraft 26.1.2 · Fabric Dev

Java 25 · Fabric Loader 0.18.4 · Fabric API 0.149.1 · Loom 1.15 · Gradle 9.4 — Official Mojang Mappings · No Yarn
Java 25 Required Official Mojang Mappings Unobfuscated Source Fabric API 0.149.1 Loom 1.15
What is Java? Key Terminology

Java is a compiled, object-oriented programming language. Minecraft is written in it. Before diving into mod code, these are the terms you'll see constantly.

Class
A blueprint for creating objects. Block, Item, Entity are all classes. Your mod files are classes too.
Object / Instance
A specific "thing" created from a class blueprint. new Block(...) creates an instance of the Block class.
Method
A function that belongs to a class. entity.hurt(...) calls the hurt method on an entity.
Field
A variable stored inside a class or object. entity.level is a field holding a reference to the world.
Interface
A contract a class must fulfill. Like a checklist — if a class implements ModInitializer, it must have onInitialize().
extends
Inheritance. class MyBlock extends Block means MyBlock is a Block with extra/overridden behavior.
implements
Fulfilling an interface contract. A class can implement many interfaces but only extend one class.
Constructor
A special method called when creating an object with new. Same name as the class, no return type.
static
Belongs to the class itself, not to any instance. static final Item MY_ITEM — there's one, shared globally.
final
Can't be reassigned. Most registered game objects are static final — register once, reference forever.
@Override
Annotation saying "I'm replacing a method from my parent class." Helps the compiler catch typos.
void
Return type meaning "this method returns nothing." onInitialize() is void — it just runs.
Generic <T>
A placeholder type. EntityType<MyEntity> means "an EntityType specifically for MyEntity."
Lambda / Arrow
A short anonymous function. state -> 10 is a tiny function that takes a state and returns 10.
Package
A namespace for organizing classes. com.mymod.blocks keeps your block classes separate from your item classes.
import
Tells Java which class you mean when you write just the short name. Your IDE adds these automatically.
Access Modifiers — Who Can See What?

Every field, method, and class in Java has a visibility level. This controls what other code can call or read it.

ModifierVisible toTypical use in Minecraft modding
publicEveryoneRegistered objects, API methods, your mod's main class
protectedSame class + subclassesMethods in Entity/Block subclasses that children override (e.g. registerGoals)
privateSame class onlyHelper methods, internal state. Mixin @Accessor or access wideners can unlock these.
(none)Same packageRare in mod code — usually use public or private explicitly.
When Minecraft's source has private fields/methods you need to access, you have two options: Access Wideners (unlock at compile time) or Mixin @Accessor / @Invoker (inject access at runtime).
Types — Primitives vs. Objects
Primitive types (lowercase, no new needed)
TypeWhat it holdsExample
intWhole numberint count = 5;
floatDecimal, less precisefloat speed = 1.5f;
doubleDecimal, more precisedouble x = 64.0;
booleantrue / falseboolean active = true;
longBig whole numberlong ticks = 72000L;
Common object types
TypeNotes
StringText. Use "double quotes".
List<T>Ordered collection. List.of(a, b, c)
Map<K,V>Key → value lookup table
Optional<T>May or may not hold a value. Avoids null crashes.
Annotations — The @ Signs

Annotations are metadata tags that change how a class or method is compiled or processed. You'll see them everywhere in Fabric mod code.

AnnotationWhat it means
@OverrideReplacing a parent class method. Compiler verifies it exists.
@NullableThis value might be null. Check before using.
@MixinMixin-specific: this class patches into a Minecraft class.
@InjectMixin: insert code at a point inside a method.
@AccessorMixin: generate a getter/setter for a private field.
@ShadowMixin: reference an existing field/method in the target class without redefining it.
@EnvironmentFabric: marks code as client-only or server-only.
How Java Code Flows in a Fabric Mod

Confused about why things go where? Here's the big picture of how Fabric loads and runs your mod.

Game launches
Fabric Loader reads
fabric.mod.json
Mixins applied to
Minecraft classes
Your onInitialize()
runs
Items/Blocks/Entities
registered
Game runs;
events fire
⚠ You can NOT use new ItemStack(...) or access most game objects during class loading (before onInitialize runs). Registration happens inside onInitialize() — not in static initializer blocks on your item class. In 26.1, even inside onInitialize, you can't create ItemStack — use ItemStackTemplate for static item references.
Java Control Flow Basics
// if / else
if (entity.isOnFire()) {
  entity.clearFire();
} else if (entity.isInWater()) {
  // ...
} else {
  // ...
}

// for loop
for (int i = 0; i < 10; i++) { /* runs 10 times */ }

// for-each (iterating a list)
for (Item item : myItemList) {
  System.out.println(item);
}

// ternary: shorthand if/else that returns a value
int light = entity.isOnFire() ? 15 : 0;
//                  ^ condition   ^ true  ^ false
Lambdas & Method References

Fabric events use lambdas heavily. A lambda is just a short way to write a function inline without naming it.

// Full anonymous class (old way — you won't write this)
event.register(new MyListener() {
  public void onTick() { doSomething(); }
});

// Lambda (modern — same thing, much shorter)
event.register(() -> doSomething());

// Lambda with parameters
event.register((server) -> {
  server.getPlayerList().broadcastMessage(...);
});

// Method reference (even shorter — "use this method")
event.register(MyMod::doSomething);

// Common in block/item registration
.lightLevel(state -> state.getValue(LIT) ? 15 : 0)
//           ^ lambda: takes BlockState, returns int
Inheritance in Minecraft — Class Tree

Minecraft's classes form deep inheritance trees. When you extend a class, you inherit all its methods and can override them. Here are the most important chains:

Entity chain
Entity ← everything
LivingEntity ← has health, equipment
Mob ← has AI goals
PathfinderMob ← can navigate
Monster ← hostile by default
Animal ← passive, breedable
Player ← the player
Block chain
Block
BaseEntityBlock ← block with a BlockEntity (chest, furnace…)
BushBlock, GrassBlock, SlabBlock← specialize behavior
Item chain
Item
SwordItem, PickaxeItem, ArmorItem
BlockItem ← item that places a block
When you see a method like entity.hurt() and you're not sure where it's defined — right-click it in VS Code/IntelliJ → "Go to Definition." You'll land on the method in the parent class that defined it.
Null, NullPointerException & Sides

Two of the most common sources of crashes in Minecraft mod code.

Null / NPE
// null = "nothing". Calling a method on null crashes!
Player player = entity.getAttackingPlayer();
player.sendMessage(...); // 💥 crash if no attacker!

// Always null-check first:
if (player != null) {
  player.sendMessage(...);
}

// Or use instanceof (also null-safe)
if (entity instanceof Player p) {
  p.sendMessage(...);
}
Client vs. Server side
⚠ Minecraft runs TWO logical environments at once: the server (handles game logic, entities, world state) and the client (handles rendering, sounds, UI). Many crashes come from calling client-only code on the server or vice versa.
// Check which side you're on:
if (!level.isClientSide()) {
  // server-side logic here (spawning, data, loot)
}

// Client-only code goes in your ClientModInitializer
// or behind the !isClientSide() guard on the client.
ResourceLocation — Namespaced IDs

Every game object (item, block, entity, sound, etc.) is identified by a ResourceLocation — a two-part ID that prevents naming conflicts between mods.

// Format: "namespace:path"
// vanilla uses "minecraft:"
// your mod uses YOUR mod id

ResourceLocation id =
  ResourceLocation.fromNamespaceAndPath("mymod", "magic_sword");
// result: "mymod:magic_sword"

// Parse from a full string:
ResourceLocation id2 = ResourceLocation.parse("minecraft:diamond");
Your mod's namespace should be its ID from fabric.mod.json — always lowercase, no spaces or special chars except underscores. Use it consistently across all resource paths.
What changed in 26.1 — The Big Picture

26.1 is the biggest Fabric toolchain shift ever. Three changes affect every single mod:

No More Obfuscation
Minecraft source is now readable with real names. No mappings layer needed. You read and write class names exactly as Mojang wrote them.
No More Yarn
Yarn was the community mapping system. It's no longer supported. All code uses Mojang's official names. Old 1.21.x Yarn code is incompatible.
modImplementation → implementation
Because there's no remapping anymore, Fabric dependencies use standard Gradle keywords. modImplementation is gone for Minecraft and Fabric API.
Parameter Names Included
Mojang's 26.1 source includes parameter names. No need for Parchment anymore — method args have real names in your IDE.
⚠ No mods built for 1.21.11 or older will work on 26.1 at all — not even as compile-only dependencies. Every mod needs a full recompile against 26.1 sources at minimum.
gradle.properties
# Core versions
minecraft_version=26.1.2
loader_version=0.18.4
fabric_version=0.149.1+26.1.2

# Mod metadata
mod_version=1.0.0
maven_group=com.yourname
archives_base_name=mymod

# Java 25 — point Gradle at your JDK 25 install
org.gradle.java.home=/path/to/jdk-25
org.gradle.jvmargs=-Xmx2G
build.gradle CORRECTED
⚠ The plugin ID changed. Use net.fabricmc.fabric-loom (not the old fabric-loom). Remove the mappings line entirely.
plugins {
  id 'net.fabricmc.fabric-loom' version "${loom_version}"
  id 'java'
}

java {
  sourceCompatibility = JavaVersion.VERSION_25
  targetCompatibility = JavaVersion.VERSION_25
}

dependencies {
  minecraft "com.mojang:minecraft:${minecraft_version}"
  // No mappings line — game is unobfuscated

  // Fabric API: use implementation (not modImplementation)
  implementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"

  // Fabric Loader: still uses modImplementation
  modImplementation "net.fabricmc:fabric-loader:${loader_version}"
}

// Use 'jar' not 'remapJar'
jar {
  from("LICENSE") { into "META-INF" }
}
Summary of keyword changes: remapJarjar · modImplementationimplementation (for Fabric API) · modCompileOnlycompileOnly · modApiapi
fabric.mod.json (26.1)
{
  "schemaVersion": 1,
  "id": "mymod",
  "version": "1.0.0",
  "name": "My Mod",
  "description": "Does cool stuff",
  "environment": "*",
  "entrypoints": {
    "main":   ["com.mymod.MyMod"],
    "client": ["com.mymod.MyModClient"]
  },
  "mixins": ["mymod.mixins.json"],
  "depends": {
    "fabric-api": ">=0.149.1",
    "fabricloader": ">=0.18.4",
    "minecraft": ">=26.1.2"
  }
}
⚠ Use "fabric-api" as the dependency ID — not "fabric". The fabric mod ID was removed.
Project File Structure
src/main/resources/
├── fabric.mod.json
├── mymod.mixins.json
├── assets/mymod/
│   ├── models/item/        ← item models (.json)
│   ├── models/block/       ← block models (.json)
│   ├── textures/item/      ← item textures (.png)
│   ├── textures/block/
│   ├── textures/entity/
│   ├── blockstates/        ← blockstate definitions
│   ├── sounds.json
│   └── lang/en_us.json     ← display names
└── data/mymod/
    ├── recipe/             ← crafting recipes
    ├── loot_table/         ← loot tables
    ├── advancement/
    ├── villager_trade/     ← new in 26.1!
    └── trade_set/          ← new in 26.1!

src/main/java/com/mymod/
├── MyMod.java              ← main entrypoint
├── MyModClient.java        ← client entrypoint
├── block/ModBlocks.java
├── item/ModItems.java
├── entity/ModEntities.java
└── mixin/
Mod Entrypoints
// Main entrypoint — runs on BOTH sides
public class MyMod implements ModInitializer {
  public static final String MOD_ID = "mymod";
  public static final Logger LOGGER =
    LoggerFactory.getLogger(MOD_ID);

  @Override
  public void onInitialize() {
    ModItems.register();
    ModBlocks.register();
    ModEntities.register();
    LOGGER.info("Mymod loaded!");
  }
}

// Client entrypoint — client ONLY (rendering, UI)
public class MyModClient implements ClientModInitializer {
  @Override
  public void onInitializeClient() {
    EntityRendererRegistry.register(
      ModEntities.MY_ENTITY, MyEntityRenderer::new);
  }
}
Useful Gradle Commands
CommandWhat it does
./gradlew runClientLaunch Minecraft client with your mod loaded
./gradlew runServerLaunch a local server with your mod
./gradlew buildCompile mod → build/libs/*.jar
./gradlew genSourcesWithVineflowerGenerate readable Minecraft source to browse in IDE
./gradlew --refresh-dependenciesForce re-download dependencies (fixes stale caches)
./gradlew wrapper --gradle-version latestUpdate Gradle wrapper
Records — Immutable Data Holders

A record is a class that holds data and nothing else. Java auto-generates constructor, getters, equals, hashCode, and toString. Great for config values, data packets, and snapshots.

// Define a record
public record SpawnData(ResourceLocation entityId, int count) {}

// Create one
SpawnData data = new SpawnData(
  ResourceLocation.parse("minecraft:zombie"), 3);

// Access fields (auto-generated getter = field name)
data.entityId(); // → minecraft:zombie
data.count();    // → 3

// Records are immutable — no setters, can't change fields
// Perfect for custom packet payloads (StreamCodec records)
Pattern Matching Switch

A supercharged switch that can match on type and destructure objects. Replaces long if/instanceof chains.

// Old way
if (entity instanceof Player) {
  Player p = (Player) entity;
  p.sendMessage(...);
} else if (entity instanceof Zombie) { ... }

// New way — cleaner, no manual casts
switch (entity) {
  case Player p          -> p.sendMessage(...);
  case Zombie z          -> z.setTarget(null);
  case LivingEntity l
    when l.isOnFire()    -> doFireStuff(l);
  default                -> {}
}
// The 'when' clause adds an extra condition to a case
Sealed Classes

A sealed class/interface declares exactly which classes can extend it. Great for representing a fixed set of states or outcomes that you want to pattern-match over.

// Declare sealed — only listed classes may implement it
public sealed interface UseResult
    permits UseResult.Success, UseResult.Fail, UseResult.Pass {

  record Success(int cooldown) implements UseResult {}
  record Fail(String reason) implements UseResult {}
  record Pass() implements UseResult {}
}

// Now switch is exhaustive — no default needed
switch (result) {
  case UseResult.Success s -> applyCooldown(s.cooldown());
  case UseResult.Fail f    -> logFailure(f.reason());
  case UseResult.Pass p   -> {}
}
Text Blocks

Multi-line strings with """. Indentation is automatically stripped. Great for inline JSON, data generation, or debug output.

// Triple-quote string — preserves formatting
String recipe = """
    {
      "type": "minecraft:crafting_shaped",
      "pattern": ["XXX", "XYX", "XXX"],
      "key": {
        "X": { "item": "minecraft:iron_ingot" },
        "Y": { "item": "mymod:magic_gem" }
      }
    }
    """;

// Useful for multi-line log messages too
LOGGER.info("""
    Mod loaded!
    Version: {}
    Blocks registered: {}
    """, mod_version, blockCount);
Virtual Threads

Virtual threads are lightweight threads managed by the JVM rather than the OS. Useful for async tasks — but never use them for Minecraft tick logic, which must stay on the main thread.

// Platform thread (old, heavy)
new Thread(() -> doWork()).start();

// Virtual thread (Java 25, lightweight)
Thread.ofVirtual().start(() -> {
  fetchDataFromExternalAPI();
});

// With a name (useful for debugging)
Thread.ofVirtual()
     .name("mymod-loader")
     .start(() -> loadConfigAsync());
⚠ Anything that touches Minecraft world state (blocks, entities, players) MUST run on the server thread. Use server.execute(() -> ...) to schedule work back onto the main thread from a virtual thread.
instanceof pattern + helpful tricks
// instanceof with capture (Java 16+, fully standard in 25)
if (entity instanceof Player p) {
  // 'p' is already cast — no manual cast needed
  p.getCooldowns().addCooldown(...);
}

// var — infers type automatically
var pos = entity.blockPosition();
// pos is BlockPos — Java figures it out

// String formatting (no more concat mess)
String msg = String.format("Entity %s at %s", entity, pos);
// or using formatted() shorthand:
String msg2 = "Entity %s at %s".formatted(entity, pos);
Registering Items
In 26.1, ItemStack can't be created until a world is loaded. For static references (creative tab icons, etc.) use ItemStackTemplate.
public class ModItems {
  public static final Item MAGIC_SWORD =
    register("magic_sword",
      new SwordItem(
        ToolMaterial.NETHERITE,
        7, -2.4f,
        new Item.Properties()));

  public static final Item MAGIC_FOOD =
    register("magic_food",
      new Item(new Item.Properties()
        .food(new FoodProperties.Builder()
          .nutrition(6).saturationModifier(1.2f)
          .build())));

  private static Item register(String name, Item item) {
    return Registry.register(
      BuiltInRegistries.ITEM,
      ResourceLocation.fromNamespaceAndPath(MyMod.MOD_ID, name),
      item);
  }

  public static void register() {
    MyMod.LOGGER.info("Registering items");
  }
}
Registering Blocks
Render layer is now automatic. ChunkSectionLayer is detected from your sprite's alpha — no more BlockRenderLayerMap calls.
public class ModBlocks {
  public static final Block CRYSTAL_BLOCK =
    register("crystal_block",
      new Block(BlockBehaviour.Properties
        .ofFullCopy(Blocks.AMETHYST_BLOCK)
        .lightLevel(state -> 10)
        .strength(3.0f, 6.0f)));

  private static Block register(String name, Block block) {
    ResourceLocation id =
      ResourceLocation.fromNamespaceAndPath(MyMod.MOD_ID, name);
    // Register BlockItem alongside the block
    Registry.register(BuiltInRegistries.ITEM, id,
      new BlockItem(block, new Item.Properties()));
    return Registry.register(
      BuiltInRegistries.BLOCK, id, block);
  }

  public static void register() {}
}
Creative Tab
// Add items to an existing tab
CreativeModeTabEvents.MODIFY_ENTRIES.register(
  CreativeModeTabs.COMBAT,
  (content, canAdd) -> {
    content.addAfter(Items.NETHERITE_SWORD,
      ModItems.MAGIC_SWORD);
  });

// Create a custom tab
Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB,
  ResourceLocation.fromNamespaceAndPath(MyMod.MOD_ID, "main"),
  CreativeModeTab.builder()
    .title(Component.translatable("itemGroup.mymod"))
    .icon(() -> new ItemStackTemplate(ModItems.MAGIC_SWORD))
    .displayItems((params, output) -> {
      output.accept(ModItems.MAGIC_SWORD);
      output.accept(ModItems.MAGIC_FOOD);
    })
    .build());
Block Color Registry (26.1) CHANGED

ColorProviderRegistry was removed. Use the new BlockColorRegistry.

// OLD (pre-26.1)
ColorProviderRegistry.BLOCK.register(
  (state, level, pos, tint) -> 0x66FF99,
  ModBlocks.MY_BLOCK);

// NEW (26.1)
BlockColorRegistry.register(
  List.of(new BlockTintSource() {
    @Override
    public int calculate(
        BlockState state,
        BlockAndTintGetter level,
        BlockPos pos,
        int tintIndex) {
      return 0x66FF99;
    }
  }),
  ModBlocks.MY_BLOCK);
Custom Entity — Full Example
// 1 — EntityType in ModEntities.java
public class ModEntities {
  public static final EntityType<MyEntity> MY_ENTITY =
    Registry.register(
      BuiltInRegistries.ENTITY_TYPE,
      ResourceLocation.fromNamespaceAndPath(MyMod.MOD_ID, "my_entity"),
      EntityType.Builder.<MyEntity>
        .of(MyEntity::new, MobCategory.MONSTER)
        .sized(0.8f, 1.8f)
        .build());

  public static void register() {
    MyMod.LOGGER.info("Registering entities");
  }
}
// 2 — MyEntity.java
public class MyEntity extends PathfinderMob {

  public MyEntity(EntityType<?> type, Level world) {
    super(type, world);
  }

  @Override
  protected void registerGoals() {
    // Priority 1 = highest. Lower number = checked first.
    this.goalSelector.addGoal(1,
      new MeleeAttackGoal(this, 1.0, true));
    this.goalSelector.addGoal(2,
      new WaterAvoidingRandomStrollGoal(this, 0.8));
    this.targetSelector.addGoal(1,
      new NearestAttackableTargetGoal<>(
        this, Player.class, true));
  }

  // Must be public static — Fabric calls this via reflection
  public static DefaultAttributeSupplier createAttributes() {
    return Monster.createMonsterAttributes()
      .add(Attributes.MAX_HEALTH, 30.0)
      .add(Attributes.ATTACK_DAMAGE, 5.0)
      .add(Attributes.MOVEMENT_SPEED, 0.28)
      .build();
  }
}
// 3 — Register attributes in onInitialize()
FabricDefaultAttributeRegistry.register(
  ModEntities.MY_ENTITY,
  MyEntity::createAttributes);
Common AI Goals
Goal ClassWhat it does
MeleeAttackGoalChase and melee-attack target
RangedAttackGoalKeep distance and ranged-attack target
WaterAvoidingRandomStrollGoalWander randomly, avoid water
LookAtPlayerGoalFace the nearest player
FloatGoalSwim when in water
NearestAttackableTargetGoalFind a target to attack
HurtByTargetGoalAttack whoever hurt you
FollowOwnerGoalTamed mob follows owner
Entity Renderer (Client)
// In MyModClient.onInitializeClient()
EntityRendererRegistry.register(
  ModEntities.MY_ENTITY,
  MyEntityRenderer::new);

// Minimal renderer extending MobRenderer
public class MyEntityRenderer
    extends MobRenderer<MyEntity, MyEntityModel<MyEntity>> {

  public MyEntityRenderer(EntityRendererProvider.Context ctx) {
    super(ctx, new MyEntityModel<>(
      ctx.bakeLayer(ModEntityModelLayers.MY_ENTITY)), 0.5f);
  }

  @Override
  public ResourceLocation getTextureLocation(MyEntity e) {
    return ResourceLocation.fromNamespaceAndPath(
      MyMod.MOD_ID, "textures/entity/my_entity.png");
  }
}
What Are Mixins?

Mixins let you inject code into Minecraft's own classes without touching their source. Think of it as surgical code grafting — you write a class that patches into a Minecraft class at compile time.

@Mixin(Target.class)
Declares your class as a patch for Target. Your class becomes part of Target at runtime.
@Inject
Insert code at a point inside a method — at the start, end, or when a specific thing is called.
@Shadow
Reference a field or method that already exists on the target class, so you can use it in your injected code.
@Accessor
Auto-generate a getter (or setter) for a private field on the target class. Accessed from outside the mixin.
@Invoker
Auto-generate a method to call a private method on the target. Accessed from outside the mixin.
CallbackInfo
Passed into @Inject methods. Call ci.cancel() to cancel a void method. Use CallbackInfoReturnable<T> for methods with return values.
mymod.mixins.json
⚠ Set compatibilityLevel to "JAVA_25" for 26.1.
{
  "required": true,
  "package": "com.mymod.mixin",
  "compatibilityLevel": "JAVA_25",
  "mixins": [
    "LivingEntityMixin"   // both sides
  ],
  "client": [
    "GameRendererMixin"   // client only
  ],
  "injectors": {
    "defaultRequire": 1   // fail build if inject point not found
  }
}
@Inject Examples
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin {

  // Cancel fall damage
  @Inject(
    method = "hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z",
    at = @At("HEAD"), cancellable = true)
  private void onHurt(
      DamageSource src, float amount,
      CallbackInfoReturnable<Boolean> cir) {
    if (src.is(DamageTypes.FALL)) {
      cir.setReturnValue(false);
    }
  }

  // Run code at method exit (TAIL = before return)
  @Inject(method = "tick()V", at = @At("TAIL"))
  private void afterTick(CallbackInfo ci) {
    // runs at end of every LivingEntity tick
  }
}
Common @At targets
ValueInjects at
HEADVery start of the method
TAILJust before every return
RETURNEach return statement
INVOKEBefore/after a specific method call inside the target
FIELDWhen a field is read or written
@Accessor & @Invoker
// @Accessor — get/set a private field
@Mixin(LivingEntity.class)
public interface LivingEntityAccessor {
  @Accessor("lastHurtMob")
  LivingEntity getLastHurtMob();

  @Accessor("lastHurtMob")
  void setLastHurtMob(LivingEntity mob);
}

// Usage — cast to the accessor interface
LivingEntity lastHurt =
  ((LivingEntityAccessor) entity).getLastHurtMob();

// @Invoker — call a private method
@Mixin(LivingEntity.class)
public interface LivingEntityInvoker {
  @Invoker("dropAllDeathLoot")
  void callDropAllDeathLoot(ServerLevel level, DamageSource src);
}
Access Wideners CORRECTED HEADER
⚠ Update the header line in your .accesswidener file: replace named with official.
# OLD (pre-26.1)
accessWidener v2 named

# NEW (26.1)
accessWidener v2 official

# Format: accessible/mutable/extendable <type> <class> [member] [descriptor]
accessible field net.minecraft.world.entity.LivingEntity
  lastHurtMob Lnet/minecraft/world/entity/LivingEntity;

accessible method net.minecraft.world.level.block.Block
  updateShape (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/LevelAccessor;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;

Access wideners are a compile-time alternative to @Accessor/@Invoker. They make private members publicly accessible to all your code — not just inside a mixin class.

Key Fabric API Events (26.1 Names) RENAMED
⚠ All Fabric API class names were updated to match Mojang's official naming. Example: ItemGroupEventsCreativeModeTabEvents. See the full rename list at docs.fabricmc.net/develop/porting/fabric-api.
EventWhen / what
ServerLifecycleEvents.SERVER_STARTEDServer fully started and ready
ServerLifecycleEvents.SERVER_STOPPINGServer is shutting down
ServerTickEvents.END_SERVER_TICKEnd of every server tick (~50ms)
ClientTickEvents.END_CLIENT_TICKEnd of every client tick
PlayerBlockBreakEvents.BEFOREBefore a block break — cancellable
UseBlockCallbackRight-clicking a block (general)
BlockEvents.USE_ITEM_ONBefore Block#useItemOn — non-null return replaces vanilla New in 26.1
BlockEvents.USE_WITHOUT_ITEMBefore Block#useWithoutItem New in 26.1
ItemEvents.USE_ONBefore Item#useOn New in 26.1
ItemEvents.USEBefore Item#use New in 26.1
DimensionEvents.MODIFY_ATTRIBUTESModify dimension-level atmosphere (clouds, fog, sky) New in 26.1
CreativeModeTabEvents.MODIFY_ENTRIESAdd/remove items from creative tabs
LootTableEvents.MODIFYInject loot into existing tables
EntityElytraEvents.ALLOWControl elytra flight on entities
HudElementRegistryRegister HUD elements (replaces removed HudRenderCallback)
AttackEntityCallbackPlayer attacking an entity
ServerEntityEvents.ENTITY_LOADEntity loaded into a level
Registering an Event

All Fabric events use the same pattern: find the event, call .register(), pass a lambda.

// In onInitialize() or a dedicated method

// Server tick example
ServerTickEvents.END_SERVER_TICK.register(server -> {
  if (server.getTickCount() % 100 == 0) {
    MyMod.LOGGER.info("100 ticks passed");
  }
});

// Block break (cancellable)
PlayerBlockBreakEvents.BEFORE.register(
    (level, player, pos, state, entity) -> {
  if (state.is(Blocks.BEDROCK)) {
    return false; // cancel the break
  }
  return true;
});

// Loot injection
LootTableEvents.MODIFY.register((key, table, source, lookup) -> {
  if (key.equals(BuiltInLootTables.SIMPLE_DUNGEON)) {
    table.pool(LootPool.lootPool()
      .add(LootItem.lootTableItem(ModItems.MAGIC_SWORD))
      .setRolls(ConstantValue.exactly(1)));
  }
});
Dimension Events (New in 26.1)
DimensionEvents.MODIFY_ATTRIBUTES.register(
  (dimension, attributes, registries) -> {
    if (dimension.is(BuiltinDimensionTypes.OVERWORLD)) {
      attributes.set(
        EnvironmentAttributes.CLOUD_COLOR,
        0xFF99FF);  // purple clouds!
    }
  });

These modifications have lower priority than biome-specific definitions, so they won't override biome mods.

Custom Packets (Networking)
// 1 — Define payload (record = great for this)
public record SyncPayload(String data)
    implements CustomPacketPayload {
  public static final CustomPacketPayload.Type<SyncPayload> TYPE =
    new CustomPacketPayload.Type<>(
      ResourceLocation.fromNamespaceAndPath(
        MyMod.MOD_ID, "sync"));
  public static final StreamCodec<FriendlyByteBuf, SyncPayload>
      CODEC = StreamCodec.composite(
        ByteBufCodecs.STRING_UTF8, SyncPayload::data,
        SyncPayload::new);
  @Override public Type<?> type() { return TYPE; }
}

// 2 — Register in onInitialize()
PayloadTypeRegistry.playS2C()
  .register(SyncPayload.TYPE, SyncPayload.CODEC);

// 3 — Send from server
ServerPlayNetworking.send(serverPlayer,
  new SyncPayload("hello!"));

// 4 — Receive on client (in ClientModInitializer)
ClientPlayNetworking.registerGlobalReceiver(
  SyncPayload.TYPE, (payload, ctx) -> {
    ctx.client().execute(() -> {
      // ctx.client().execute() = safely back on client thread
      MyMod.LOGGER.info(payload.data());
    });
  });
Official Mappings — Key Class Reference
World / Level
ClassNotes
LevelBase world (was World in Yarn)
ServerLevelServer-side world — use for spawning, data
ClientLevelClient-side world — use for rendering
MinecraftServerServer instance
BlockPosImmutable int block coordinate
Vec33D double vector (precise position)
ChunkPosChunk coordinate (BlockPos / 16)
Block / Item
ClassNotes
BlockBlock definition/behavior
BlockStateBlock + all property values at a location
BlockBehaviour.PropertiesSettings builder (strength, light, sound…)
ItemItem definition
Item.PropertiesItem settings builder
ItemStackItem + count + components. Needs world loaded.
ItemStackTemplateSafe static item reference before world load New in 26.1
DataComponentsBuilt-in component keys (damage, enchantments…)
Registries
ClassNotes
BuiltInRegistriesStatic access to ITEM, BLOCK, ENTITY_TYPE, BIOME…
Registry.register()Register any game object
ResourceLocationNamespaced ID. fromNamespaceAndPath("mod","id")
ResourceKeyTyped registry key (for datapacks, dimensions)
Entities
ClassNotes
EntityBase for everything in the world
LivingEntityHas health, food, equipment slots
PathfinderMobMob with goal-based pathfinding AI
MonsterHostile mob base (attacks players by default)
AnimalPassive mob base (breedable)
PlayerAbstract base player
ServerPlayerServer-side player (use for sending packets, data)
AttributesMAX_HEALTH, ATTACK_DAMAGE, MOVEMENT_SPEED, ARMOR…
MobCategoryMONSTER, CREATURE, AMBIENT, WATER_CREATURE, MISC…
Damage & Combat
ClassNotes
DamageSourceDescribes what caused damage (entity, fire, fall…)
DamageTypesBuilt-in damage type registry keys
Text & Chat
ClassNotes
ComponentMinecraft's rich text object
Component.literal("...")Plain text component
Component.translatable("key")Localized text component (uses lang file)
Recipes (26.1 simplified)
ClassNotes
RecipeSerializerNow wraps MapCodec + StreamCodec directly Changed in 26.1
BuiltInRegistries.RECIPE_SERIALIZERRegister custom recipe serializers
Networking
ClassNotes
ServerPlayNetworkingSend packets to a client from the server
ClientPlayNetworkingSend packets to the server; receive from server
PayloadTypeRegistryRegister your custom packet payload type
CustomPacketPayloadInterface your packet data class implements
Data-Driven Features (No Java Needed)

Several systems moved to datapacks in 26.1. Just create JSON files — no API registration required.

FeatureData path
Villager tradesdata/<ns>/villager_trade/ + add to tag at data/minecraft/tags/villager_trade/<profession>
Trade setsdata/<ns>/trade_set/
Recipesdata/<ns>/recipe/ — JSON as before, serializer API simplified
Loot tablesdata/<ns>/loot_table/
Advancementsdata/<ns>/advancement/
Tagsdata/<ns>/tags/<registry>/
TradeOfferHelper is deprecated in 26.1. Use the villager_trade datapack system instead.
26.1-Specific Gotchas
  • No ItemStack before world load — use ItemStackTemplate for creative tab icons and static fields. new ItemStack(item) crashes before a world is loaded.
  • Yarn is gone — any method/class named "method_XXXXX" or "field_XXXXX" is a stale Yarn reference from old code. All names are now Mojang official.
  • Source includes parameter names — no need for Parchment. Parameter names are present in the 26.1 source. Just generate sources with Vineflower.
  • Render layers are automatic — ChunkSectionLayer is auto-detected from sprite alpha. Don't register render layers manually.
  • IntelliJ 2025.3+ required for full Java 25 mixin support — older versions break mixin processing.
  • modImplementation → implementation (for Fabric API) — Fabric Loader itself still uses modImplementation, but Fabric API and mod-to-mod deps use plain implementation.
  • OpenGL direct calls will break in 26.2 — Vulkan support is coming next major version. If you use raw GL calls (not through Blaze3D API), start migrating now.
  • Villager trades → datapacksTradeOfferHelper is deprecated. Add JSON to data/<ns>/villager_trade/.
  • Model Loading API may lag behind — Fabric's Model Loading API and Renderer/Indigo modules may not be fully available in early 26.1.x patch releases. Check the Fabric Discord.
  • HudRenderCallback removed — replaced by HudElementRegistry.
  • Access widener header must say 'official' — update from accessWidener v2 named to accessWidener v2 official or your build fails.
  • No mods from 1.21.11 or older will work at all — not even as compile-only dependencies. Every mod needs a recompile at minimum.
Common Java Mistakes for Beginners
  • Calling methods on null — always check if (x != null) or use instanceof before using a value that might be absent. Most common crash cause.
  • Doing game logic on the wrong side — rendering code on the server crashes. World-modifying code from a client thread corrupts state. Use !level.isClientSide() guards.
  • Forgetting to call register() — declaring a static final block/item but forgetting to call ModBlocks.register() in onInitialize() means it's never added to the registry.
  • Missing import — if VS Code shows "cannot find symbol," you probably need to import the class. Use the quick-fix lightbulb or Ctrl+.
  • Wrong method signature in @Inject — Mixin won't find the injection point and will warn or crash. Copy the exact method descriptor from generated sources.
  • Forgetting f on float literals0.5 is a double. 0.5f is a float. Minecraft uses floats a lot for block/entity sizing.
  • Using == on Stringsstr == "hello" compares identity, not content. Use str.equals("hello").
Debugging Tips
  • Read the crash report top-to-bottom. The cause is near the bottom under "Caused by:". Ignore the stack of indirection above it.
  • Use MyMod.LOGGER.info(...) liberally. Logs show in the console during runClient.
  • Run ./gradlew genSourcesWithVineflower once after setup. This lets your IDE navigate directly into Minecraft source — invaluable for understanding what methods do and finding correct signatures.
  • Right-click → "Go to Definition" in VS Code (with Java extension) or IntelliJ to jump to any Minecraft class or method.
  • If Gradle fails with a dependency error, try ./gradlew --refresh-dependencies to clear the cache.
  • Mixin injection not firing? Check your mymod.mixins.json has the class listed and compatibilityLevel is "JAVA_25".
Resources
ResourceURL / command
Fabric Docsdocs.fabricmc.net
Porting Guidedocs.fabricmc.net/develop/porting
API Rename Guidedocs.fabricmc.net/develop/porting/fabric-api
Example Modfabricmc.net/develop/template/
Fabric Wikiwiki.fabricmc.net
Fabric Discorddiscord.fabricmc.net
Minecraft Wiki (class search)minecraft.wiki
Generate sources./gradlew genSourcesWithVineflower
Run client./gradlew runClient
Build mod jar./gradlew buildbuild/libs/*.jar