Getting Started with Forge
Why Choose Forge?
Forge offers several advantages for Minecraft modding:
- Mature ecosystem - Forge has been around for many years with a large community and extensive documentation
- Powerful API - Comprehensive set of tools for modifying almost every aspect of Minecraft
- Mod compatibility - Forge mods are designed to work together through a robust event system
- Stable development - Well-established development practices and patterns
- Large community - Extensive community support and a wealth of existing mods to learn from
Setting Up Your Development Environment
Setting up for Forge development involves a few key steps:
- Install Java Development Kit (JDK) 17 or higher
Forge 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 Forge MDK
Download the Minecraft Development Kit (MDK) for your target Minecraft version from the official Forge 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
Forge Mod Basics
Mod Structure
A basic Forge 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 Forge events
- Resources - Textures, models, and other assets
Here's an example of a mods.toml file:
# This is an example mods.toml file
modLoader="javafml"
loaderVersion="[43,)"
license="MIT"
[[mods]]
modId="examplemod"
version="1.0.0"
displayName="Example Mod"
authors="Your Name"
description='''
This is an example mod for Minecraft Forge!
'''
[[dependencies.examplemod]]
modId="forge"
mandatory=true
versionRange="[43,)"
ordering="NONE"
side="BOTH"
[[dependencies.examplemod]]
modId="minecraft"
mandatory=true
versionRange="[1.19.2,1.20)"
ordering="NONE"
side="BOTH"
Main Mod Class
The main class is the entry point for your mod. Here's a simple example:
package com.example.examplemod;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
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() {
// Get the mod event bus
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
// Register ourselves for server and other game events
MinecraftForge.EVENT_BUS.register(this);
// Register the setup method for mod loading
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!");
}
}
Registering Items and Blocks
Here's how to register items and blocks in Forge:
package com.example.examplemod;
import net.minecraft.world.item.Item;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.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.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.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 Forge Concepts
Event System
Forge's event system is a powerful way to hook into Minecraft's code. 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.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@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 Forge
Forge provides a robust networking system for communication between client and server:
package com.example.examplemod.networking;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel;
import java.util.function.BiConsumer;
import java.util.function.Function;
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
Forge provides a built-in configuration system:
package com.example.examplemod;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.config.ModConfig;
import org.apache.commons.lang3.tuple.Pair;
public class ModConfig {
public static class Common {
public final ForgeConfigSpec.BooleanValue enableFeature;
public final ForgeConfigSpec.IntValue cooldownTicks;
public final ForgeConfigSpec.ConfigValue<String> welcomeMessage;
public Common(ForgeConfigSpec.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 ForgeConfigSpec COMMON_SPEC;
public static final Common COMMON;
static {
final Pair<Common, ForgeConfigSpec> specPair = new ForgeConfigSpec.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() {
// Register config
ModConfig.register();
// Rest of your initialization code
}