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:
- Install Java Development Kit (JDK) 17 or higher
NeoForge for modern Minecraft versions requires JDK 17 or higher. Download it from Adoptium or Oracle.
- Choose an IDE
We recommend IntelliJ IDEA or Eclipse for Java development.
- Download the NeoForge MDK
Download the Minecraft Development Kit (MDK) for your target Minecraft version from the official NeoForge website.
- Set up the project
Extract the MDK and run the appropriate setup command for your environment:
Setup Commands12345678910# 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:
# 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:
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:
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:
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:
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:
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:
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:
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:
- Update package names
Replace
net.minecraftforge
withnet.neoforged.neoforge
andnet.minecraftforge.eventbus
withnet.neoforged.bus
. - Update mod constructor
The mod constructor now receives the mod event bus as a parameter.
- Update mods.toml
Change the dependency from
forge
toneoforge
and update the version range. - 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.