Getting Started with NeoForge

Why Choose NeoForge?

NeoForge offers several advantages for Minecraft modding:

  • Community-driven development - NeoForge is developed by and for the modding community
  • Transparent governance - Open decision-making process and clear roadmap
  • Forge compatibility - Most Forge mods work with minimal or no changes on NeoForge
  • Modern codebase - Cleaner, more maintainable code with better documentation
  • Active development - Regular updates and responsive to community feedback

Setting Up Your Development Environment

Setting up for NeoForge development involves a few key steps:

  1. Install Java Development Kit (JDK) 17 or higher

    NeoForge for modern Minecraft versions requires JDK 17 or higher. Download it from Adoptium or Oracle.

  2. Choose an IDE

    We recommend IntelliJ IDEA or Eclipse for Java development.

  3. Download the NeoForge MDK

    Download the Minecraft Development Kit (MDK) for your target Minecraft version from the official NeoForge website.

  4. Set up the project

    Extract the MDK and run the appropriate setup command for your environment:

    Setup Commands
    12345678910
    # For Windows
    gradlew genEclipseRuns
    gradlew eclipse
    
    # For IntelliJ IDEA
    gradlew genIntellijRuns
    gradlew idea
    
    # For general setup
    gradlew setup

NeoForge Mod Basics

Mod Structure

A basic NeoForge mod consists of the following components:

  • mods.toml - The mod metadata file
  • Main mod class - The entry point for your mod
  • Event handlers - Classes that respond to NeoForge events
  • Resources - Textures, models, and other assets

Here's an example of a mods.toml file for NeoForge:

mods.toml
123456789101112131415161718192021222324252627
# This is an example mods.toml file for NeoForge
modLoader="javafml" 
loaderVersion="[1,)" 
license="MIT"

[[mods]] 
modId="examplemod" 
version="1.0.0" 
displayName="Example Mod" 
authors="Your Name" 
description='''
This is an example mod for Minecraft NeoForge!
'''

[[dependencies.examplemod]] 
    modId="neoforge" 
    mandatory=true 
    versionRange="[20.2,)" 
    ordering="NONE"
    side="BOTH"

[[dependencies.examplemod]]
    modId="minecraft"
    mandatory=true
    versionRange="[1.20.2,1.21)"
    ordering="NONE"
    side="BOTH"

Main Mod Class

The main class is the entry point for your mod. Here's a simple example for NeoForge:

ExampleMod.java
123456789101112131415161718192021222324252627
package com.example.examplemod;

import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Mod("examplemod")
public class ExampleMod {
    // Create a logger
    private static final Logger LOGGER = LogManager.getLogger();

    public ExampleMod(IEventBus modEventBus) {
        // Register ourselves for server and other game events
        modEventBus.addListener(this::setup);
        
        // Register item and block registries
        ModItems.register(modEventBus);
        ModBlocks.register(modEventBus);
    }

    private void setup(final FMLCommonSetupEvent event) {
        // Do initial setup
        LOGGER.info("Example mod initialized!");
    }
}

Note the differences from Forge: NeoForge uses the net.neoforged package instead of net.minecraftforge, and the constructor receives the mod event bus directly.

Registering Items and Blocks

Here's how to register items in NeoForge:

ModItems.java
12345678910111213141516171819202122
package com.example.examplemod;

import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.neoforged.neoforge.registries.ForgeRegistries;
import net.neoforged.neoforge.registries.RegistryObject;

public class ModItems {
    // Create a Deferred Register for items
    public static final DeferredRegister<Item> ITEMS = 
        DeferredRegister.create(ForgeRegistries.ITEMS, "examplemod");
    
    // Register a new item
    public static final RegistryObject<Item> EXAMPLE_ITEM = 
        ITEMS.register("example_item", () -> new Item(new Item.Properties()));
    
    // Method to register all items
    public static void register(IEventBus eventBus) {
        ITEMS.register(eventBus);
    }
}

And for blocks:

ModBlocks.java
123456789101112131415161718192021222324252627282930313233343536373839404142
package com.example.examplemod;

import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.neoforged.neoforge.registries.ForgeRegistries;
import net.neoforged.neoforge.registries.RegistryObject;

import java.util.function.Supplier;

public class ModBlocks {
    // Create a Deferred Register for blocks
    public static final DeferredRegister<Block> BLOCKS =
        DeferredRegister.create(ForgeRegistries.BLOCKS, "examplemod");
    
    // Register a new block
    public static final RegistryObject<Block> EXAMPLE_BLOCK = registerBlock("example_block",
        () -> new Block(BlockBehaviour.Properties.of(Material.STONE)
            .strength(2f)
            .requiresCorrectToolForDrops()));
    
    // Helper method to register a block and its item form
    private static <T extends Block> RegistryObject<T> registerBlock(String name, Supplier<T> block) {
        RegistryObject<T> registeredBlock = BLOCKS.register(name, block);
        registerBlockItem(name, registeredBlock);
        return registeredBlock;
    }
    
    // Helper method to register a block item
    private static <T extends Block> void registerBlockItem(String name, RegistryObject<T> block) {
        ModItems.ITEMS.register(name, () -> new BlockItem(block.get(), new Item.Properties()));
    }
    
    // Method to register all blocks
    public static void register(IEventBus eventBus) {
        BLOCKS.register(eventBus);
    }
}

Advanced NeoForge Concepts

Event System

NeoForge's event system is similar to Forge's but with some package changes. Here's how to create an event handler:

ModEvents.java
12345678910111213141516171819202122232425
package com.example.examplemod;

import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;

@Mod.EventBusSubscriber(modid = "examplemod")
public class ModEvents {
    
    @SubscribeEvent
    public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
        Player player = event.getEntity();
        player.sendSystemMessage(Component.literal("Hello, " + player.getName().getString() + "!"));
    }
    
    @SubscribeEvent
    public static void onPlayerBreakSpeed(PlayerEvent.BreakSpeed event) {
        // Increase mining speed by 50% when the player is below Y=0
        if (event.getEntity().getY() < 0) {
            event.setNewSpeed(event.getOriginalSpeed() * 1.5f);
        }
    }
}

Networking in NeoForge

NeoForge provides a networking system similar to Forge but with updated package names:

ModMessages.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
package com.example.examplemod.networking;

import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.NetworkEvent;
import net.neoforged.neoforge.network.NetworkRegistry;
import net.neoforged.neoforge.network.PacketDistributor;
import net.neoforged.neoforge.network.simple.SimpleChannel;

import java.util.function.Supplier;

public class ModMessages {
    private static final String PROTOCOL_VERSION = "1";
    
    // Create a new SimpleChannel for our mod's packets
    public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
        new ResourceLocation("examplemod", "main"),
        () -> PROTOCOL_VERSION,
        PROTOCOL_VERSION::equals,
        PROTOCOL_VERSION::equals
    );
    
    private static int packetId = 0;
    private static int id() {
        return packetId++;
    }
    
    public static void register() {
        // Register packets
        INSTANCE.registerMessage(
            id(),
            ExamplePacket.class,
            ExamplePacket::encode,
            ExamplePacket::decode,
            ExamplePacket::handle
        );
    }
    
    // Send a packet to the server
    public static void sendToServer(Object packet) {
        INSTANCE.sendToServer(packet);
    }
    
    // Send a packet to a specific player
    public static void sendToPlayer(Object packet, ServerPlayer player) {
        INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
    }
    
    // Example packet class
    public static class ExamplePacket {
        private final String message;
        
        public ExamplePacket(String message) {
            this.message = message;
        }
        
        public static void encode(ExamplePacket packet, FriendlyByteBuf buffer) {
            buffer.writeUtf(packet.message);
        }
        
        public static ExamplePacket decode(FriendlyByteBuf buffer) {
            return new ExamplePacket(buffer.readUtf());
        }
        
        public static void handle(ExamplePacket packet, Supplier<NetworkEvent.Context> contextSupplier) {
            NetworkEvent.Context context = contextSupplier.get();
            context.enqueueWork(() -> {
                // This code runs on the receiving side
                System.out.println("Received message: " + packet.message);
            });
            context.setPacketHandled(true);
        }
    }
}

Config Files

NeoForge provides a configuration system similar to Forge:

ModConfig.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
package com.example.examplemod;

import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.neoforge.common.ModConfigSpec;
import org.apache.commons.lang3.tuple.Pair;

public class ModConfig {
    public static class Common {
        public final ModConfigSpec.BooleanValue enableFeature;
        public final ModConfigSpec.IntValue cooldownTicks;
        public final ModConfigSpec.ConfigValue<String> welcomeMessage;
        
        public Common(ModConfigSpec.Builder builder) {
            builder.comment("Common configuration settings")
                   .push("common");
            
            enableFeature = builder
                .comment("Whether to enable the example feature")
                .define("enableFeature", true);
            
            cooldownTicks = builder
                .comment("Cooldown in ticks for the example feature")
                .defineInRange("cooldownTicks", 20, 1, 200);
            
            welcomeMessage = builder
                .comment("Welcome message to display to players")
                .define("welcomeMessage", "Hello, world!");
            
            builder.pop();
        }
    }
    
    public static final ModConfigSpec COMMON_SPEC;
    public static final Common COMMON;
    
    static {
        final Pair<Common, ModConfigSpec> specPair = new ModConfigSpec.Builder()
            .configure(Common::new);
        COMMON_SPEC = specPair.getRight();
        COMMON = specPair.getLeft();
    }
    
    public static void register() {
        ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, COMMON_SPEC);
    }
}

Then in your main mod class, register the config:

Config Registration
123456
public ExampleMod(IEventBus modEventBus) {
    // Register config
    ModConfig.register();
    
    // Rest of your initialization code
}

Migrating from Forge to NeoForge

If you're migrating an existing Forge mod to NeoForge, here are the key changes you'll need to make:

  1. Update package names

    Replace net.minecraftforge with net.neoforged.neoforge and net.minecraftforge.eventbus with net.neoforged.bus.

  2. Update mod constructor

    The mod constructor now receives the mod event bus as a parameter.

  3. Update mods.toml

    Change the dependency from forge to neoforge and update the version range.

  4. Update build.gradle

    Update the Gradle dependencies to use NeoForge instead of Forge.

Most other code should work without changes, as NeoForge maintains API compatibility with Forge where possible.