Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.16.1] The Book of Minecraft Modding - complete guide for beginners


 Share

Recommended Posts

I got into minecraft modding recently and was surprised by how hard it is to find useful up-to-date information. Because of that, I decided to document my learning journey and write a book about Minecraft modding for beginners. It applies to Minecraft 1.16.1 and the latest version of forge.

 

Keep in mind it is still a work in progress. More chapters will be added in the coming days. Suggestions and questions also are welcomed.

 

Link: https://thebookofmodding.ml/

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

6 hours ago, ChampionAsh5357 said:

Please use object holder to "hold" the block and item instances when registering or use deferred register if you're going to statically initialize anything. What you have written for custom blocks is just wrong.

I register my items and blocks similar:

 

RegisterBlocks.java

package maxi.ores_cores.init;

import maxi.ores_cores.OresCores;
import maxi.ores_cores.blocks.*;
import maxi.ores_cores.items.BasicBlockItem;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraftforge.event.RegistryEvent.Register;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.registries.IForgeRegistry;

@EventBusSubscriber(modid = OresCores.MODID, bus = Bus.MOD)
public class RegisterBlocks {

	@SubscribeEvent
	public static void registerBlock(Register<Block> event) {
		final IForgeRegistry<Block> registry = event.getRegistry();
		registry.register(MetalBlock.EMOULRITE_BLOCK.setRegistryName(OresCores.MODID, "emoulrite_block"));
		registry.register(Ore.EMOULRITE_ORE.setRegistryName(OresCores.MODID, "emoulrite_ore"));
		registry.register(MetalBlock.TELERITE_BLOCK.setRegistryName(OresCores.MODID, "telerite_block"));
		registry.register(Ore.TELERITE_ORE.setRegistryName(OresCores.MODID, "telerite_ore"));
	}
	
	@SubscribeEvent
	public static void registerItem(Register<Item> event) {
		final IForgeRegistry<Item> registry = event.getRegistry();
		registry.register(new BasicBlockItem(MetalBlock.EMOULRITE_BLOCK, ItemGroup.BUILDING_BLOCKS));
		registry.register(new BasicBlockItem(Ore.EMOULRITE_ORE, ItemGroup.BUILDING_BLOCKS));
		registry.register(new BasicBlockItem(MetalBlock.TELERITE_BLOCK, ItemGroup.BUILDING_BLOCKS));
		registry.register(new BasicBlockItem(Ore.TELERITE_ORE, ItemGroup.BUILDING_BLOCKS));
	}
}

 

MetalBlock.java (Ore.java is similar)

package maxi.ores_cores.blocks;

import net.minecraft.block.SoundType;
import net.minecraft.block.Block;
import net.minecraft.block.Block.Properties;
import net.minecraft.block.material.Material;
import net.minecraft.block.material.MaterialColor;
import net.minecraftforge.common.ToolType;

public class MetalBlock {
	
	public static final Block EMOULRITE_BLOCK = new Block(Properties
								.create(Material.IRON, MaterialColor.GOLD).sound(SoundType.METAL)
								.hardnessAndResistance(5, 6)
								.harvestTool(ToolType.PICKAXE).harvestLevel(2));
	
	public static final Block TELERITE_BLOCK = new Block(Properties
								.create(Material.IRON, MaterialColor.PURPLE).sound(SoundType.METAL)
								.hardnessAndResistance(5, 6)
								.harvestTool(ToolType.PICKAXE).harvestLevel(2));
	
}

 

BasicBlockItem.java

package maxi.ores_cores.items;

import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemGroup;

public class BasicBlockItem extends BlockItem {

	public BasicBlockItem(Block block, ItemGroup group) {
		super(block, new Properties().group(group));
		setRegistryName(block.getRegistryName());
	}

}

 

Whats bad about this? I am new to modding and I appreciate every suggestion for improvement that I get

Link to comment
Share on other sites

3 minutes ago, diesieben07 said:

Registration must occur during a very specific time (when the registry event is fired). By using a static initializer you are completely bypassing this and doing it when your class is initialized, which is not easy to know when it is.

So I should make the methods that register the blocks and items non static?

Link to comment
Share on other sites

2 hours ago, Maxi07 said:

public static final Block EMOULRITE_BLOCK = new Block(Properties .create(Material.IRON, MaterialColor.GOLD).sound(SoundType.METAL) .hardnessAndResistance(5, 6) .harvestTool(ToolType.PICKAXE).harvestLevel(2));

Oh okay, you mean this, right?

 

EDIT: Thanks for helping me improving my code @diesieben07

Edited by Maxi07
Link to comment
Share on other sites

On 7/4/2020 at 10:11 AM, ChampionAsh5357 said:

Please use object holder to "hold" the block and item instances when registering or use deferred register if you're going to statically initialize anything. What you have written for custom blocks is just wrong.

Thanks for pointing this out!

 

I updated the tutorial to use deferred registers.

 

Please let me know if you spot any further errors. I am happy to correct them as well

Link to comment
Share on other sites

On 7/4/2020 at 1:09 AM, tcode2k16 said:

I got into minecraft modding recently and was surprised by how hard it is to find useful up-to-date information. Because of that, I decided to document my learning journey and write a book about Minecraft modding for beginners. It applies to Minecraft 1.16.1 and the latest version of forge.

 

Keep in mind it is still a work in progress. More chapters will be added in the coming days. Suggestions and questions also are welcomed.

 

Link: https://thebookofmodding.ml/

I want to know how to create and register an Events

Link to comment
Share on other sites

41 minutes ago, zenglintao said:

I want to know how to create and register an Events

Extend Event, then call MinecraftForge#EVENT_BUS#register to register it, remember you have to fire your event manually using MinecraftForge#EVENT_BUS#post.

Edited by Novârch

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

On 7/4/2020 at 4:11 AM, ChampionAsh5357 said:

Please use object holder to "hold" the block and item instances when registering or use deferred register if you're going to statically initialize anything.

Okay, now I am finally continue with modding. I am changing my whole registration system and how I add blocks and items. I dont want the static intializer problem, but I dont want to use DefferedRegisries either (please dont ask why). So I looked at the minecraft classes net.minecraft.block.Blocks and net.minecraft.item.Items where the minecraft blocks and items are registered. I want to do this similar. I should use ObjectHolders. I searched how they work but I couldn't find something that could help me. And now I am asking here. And please say if you have an idea how i could make my registration system without DefferedRegisries and problems.

Thanks!

Edited by Maxi07
Link to comment
Share on other sites

5 minutes ago, Maxi07 said:

I dont want the static intializer problem, but I dont want to use DefferedRegisries either

RegistryEvent, don't use vanilla registration if there is a Forge version available.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

6 minutes ago, Maxi07 said:

I want to do this similar.

You can't, vanilla is different.

 

If you do not want to use DeferredRegister (yes, I will ask why), you need to subscribe to RegistryEvent.Register for the appropriate type (e.g. RegistryEvent.Register<Block>) and register your things there.

Then to get hold of your instances either use @ObjectHolder (put it on a class, then the annotation value must be your mod ID and field names become the registry names for what to inject, fields do not need annotations; otherwise put it on individual fields, then the annotation value must be the full registry name including mod ID).

Otherwise you can also use RegistryObject:

public static final RegistryObject<Block> MY_COOL_BLOCK = RegistryObject.of(new ResourceLocation(...), ForgeRegistries.BLOCKS)

 

Link to comment
Share on other sites

So will this already work?

package maxi.ores_cores.init;

import maxi.ores_cores.OresCores;
import maxi.ores_cores.items.BasicBlockItem;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.Block.Properties;
import net.minecraft.block.material.Material;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.event.RegistryEvent.Register;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.ObjectHolder;

@EventBusSubscriber(modid = OresCores.MODID, bus = Bus.MOD)
@ObjectHolder(value = OresCores.MODID)
public class RegisterBlocks {

	public static final Block EXAMPLE_BLOCK = new Block(Properties.create(Material.ROCK, MaterialColor.STONE).sound(SoundType.STONE).hardnessAndResistance(3, 3).harvestTool(ToolType.PICKAXE).harvestLevel(2));
	
	@SubscribeEvent
	public static void registerBlock(Register<Block> event) {
		final IForgeRegistry<Block> registry = event.getRegistry();
		registry.register(EXAMPLE_BLOCK.setRegistryName(OresCores.MODID, "emoulrite_block"));
	}
	
	@SubscribeEvent
	public static void registerItem(Register<Item> event) {
		final IForgeRegistry<Item> registry = event.getRegistry();
		registry.register(new BasicBlockItem(EXAMPLE_BLOCK, ItemGroup.BUILDING_BLOCKS));
	}
}

 

Link to comment
Share on other sites

You create the instance in the registry event and then use @ObjectHolder or RegistryObject to obtain it outside.

In the future the registry events might fire again and then you need to re-register your things and Forge will automatically update any fields using @ObjectHolder or any RegistryObject. That will hopefully, eventually, enable the runtime loading and/or disabling/enabling of mods.

Link to comment
Share on other sites

29 minutes ago, diesieben07 said:

Then to get hold of your instances either use @ObjectHolder (put it on a class, then the annotation value must be your mod ID and field names become the registry names for what to inject, fields do not need annotations; otherwise put it on individual fields, then the annotation value must be the full registry name including mod ID).

Otherwise you can also use RegistryObject:


public static final RegistryObject<Block> MY_COOL_BLOCK = RegistryObject.of(new ResourceLocation(...), ForgeRegistries.BLOCKS)

 

Link to comment
Share on other sites

Now it finally works! Every tutorial I've seen used static initializers. What are the problems, why I should avoid them (I know you said something about timing above)? And by the way, is my code now finally not bad?

@EventBusSubscriber(modid = OresCores.MODID, bus = Bus.MOD)
public class RegisterBlocks {

	@SubscribeEvent
	public static void registerBlock(Register<Block> event) {
		
		final Block EXAMPLE_BLOCK = new Block(Properties.create(Material.ROCK, MaterialColor.STONE).sound(SoundType.STONE).hardnessAndResistance(3, 3).harvestTool(ToolType.PICKAXE).harvestLevel(2));
		
		final IForgeRegistry<Block> registry = event.getRegistry();
		registry.register(EXAMPLE_BLOCK.setRegistryName(OresCores.MODID, "example_block"));
	}
	
	@SubscribeEvent
	public static void registerItem(Register<Item> event) {
		final IForgeRegistry<Item> registry = event.getRegistry();
		registry.register(new BasicBlockItem(Blocks.EXAMPLE_BLOCK, ItemGroup.BUILDING_BLOCKS));
	}
}

Blocks.java:

@ObjectHolder(value = OresCores.MODID)
public class Blocks {

	public static final Block EXAMPLE_BLOCK = null;
	
}

 

Link to comment
Share on other sites

48 minutes ago, Maxi07 said:

Every tutorial I've seen used static initializers.

Those tutorials are bad then. Unfortunately most modding tutorials are written by amateurs, who don't even come here to ask for feedback before posting it as just "the way to do it" on Youtube, etc.

 

48 minutes ago, Maxi07 said:

What are the problems, why I should avoid them (I know you said something about timing above)?

Registration must happen exactly when Forge tells you. Forge does so by firing the registry event. If you use a static initializer you completely bypass this system. This prevents Forge from doing improvements in the future, such as reloading mods at runtime or even disabling/enabling them.

 

48 minutes ago, Maxi07 said:

And by the way, is my code now finally not bad?

Yes, it's fine now.

However if you do write a tutorial I would recommend you use DeferredRegister or at least RegistryObject as they are the current recommended approach. "Magic injection into fields" using @ObjectHolder is ugly. Why do you not want to use them?

  • Thanks 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I mean, you can, it would be a different forge mod loader version. I am asking mostly because I refuse to change to 1.19.x due to Microsoft's policies and decisions about the game (the in-game chat report system)
    • The main task - is to make your own gui for the inventory or just a separate gui, which will implement the basic functions for the marketplace (as in the various plugins). This needs to be implemented specifically by a mod. So far I've tried creating a class that inherits the Screen and setting as new ( minecraft.setScreen(new CustomScreen())), but the main inventory stops working (not moving things from slot to slot). Also tried overriding the InventoryScreen class, but the problem is the same as above. Decided to start simple, namely adding a button to the main inventory. Logic: listening for the ScreenEvent event, and setting the new button as a widget.  @SubscribeEvent public static void openInventoryGui(ScreenEvent e) { Minecraft minecraft = Minecraft.getInstance(); if (minecraft.screen != null) { Screen current = e.getScreen(); if (current.passEvents) { Button button = new Button(10, 10, 10, 10, Component.literal("TEST"), (a) -> { System.out.println("Press"); }); e.getScreen().renderables.add(button); } } } But so far the button does not work (should output text to the console).  Regarding ScreenEvent.InitScreenEvent - I could not do anything with it (it is not in the class, found no such event). What can I read or what guides to look at this topic? Or maybe someone has advice or experience in this matter?
    • FontManager although the details are in the GlyphProvider implementations.
    • What class handles the minecraft/font/default.json file?
    • forge server crashed   [26Sep2022 21:58:48.509] [main/ERROR] [net.minecraftforge.eventbus.EventBus/EVENTBUS]: Exception caught during firing event: 'net.minecraft.commands.Commands$CommandSelection net.minecraftforge.event.RegisterCommandsEvent.getEnvironment()' 98Index: 9 99Listeners: 1000: HIGH 1011: ASM: class dev.architectury.event.forge.EventHandlerImplCommon event(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 1022: NORMAL 1033: net.minecraftforge.eventbus.EventBus$$Lambda$4098/0x0000000801640998@f45879e 1044: ASM: class ovh.corail.tombstone.event.EventHandler onRegisterCommand(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 1055: ASM: class de.markusbordihn.playercompanions.commands.CommandManager handleRegisterCommandsEvent(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 1066: net.minecraftforge.eventbus.EventBus$$Lambda$4098/0x0000000801640998@296caa2a 1077: ASM: net.minecraftforge.common.ForgeInternalHandler@495ae68d onCommandsRegister(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 1088: net.minecraftforge.eventbus.EventBus$$Lambda$4098/0x0000000801640998@47ac9558 1099: ASM: class potionstudios.byg.BYGForgeEventsHandler registerCommands(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 11010: ASM: journeymap.common.events.forge.ForgeServerEvents@7253729a registerCommandEvent(Lnet/minecraftforge/event/RegisterCommandsEvent;)V 111java.lang.NoSuchMethodError: 'net.minecraft.commands.Commands$CommandSelection net.minecraftforge.event.RegisterCommandsEvent.getEnvironment()' 112at TRANSFORMER/byg@2.0.0.5/potionstudios.byg.BYGForgeEventsHandler.registerCommands(BYGForgeEventsHandler.java:63) 113at TRANSFORMER/byg@2.0.0.5/potionstudios.byg.__BYGForgeEventsHandler_registerCommands_RegisterCommandsEvent.invoke(.dynamic) 114at MC-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:93) 115at MC-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) 116at MC-BOOTSTRAP/net.minecraftforge.eventbus/net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) 117at TRANSFORMER/forge@41.1.0/net.minecraftforge.event.ForgeEventFactory.onCommandRegister(ForgeEventFactory.java:647) 118at TRANSFORMER/minecraft@1.19/net.minecraft.commands.Commands.<init>(Commands.java:202) 119at TRANSFORMER/minecraft@1.19/net.minecraft.server.ReloadableServerResources.<init>(ReloadableServerResources.java:44) 120at TRANSFORMER/minecraft@1.19/net.minecraft.server.ReloadableServerResources.m_206861_(ReloadableServerResources.java:86) 121at TRANSFORMER/minecraft@1.19/net.minecraft.server.WorldLoader.m_214362_(WorldLoader.java:28) 122at TRANSFORMER/minecraft@1.19/net.minecraft.server.WorldStem.m_214415_(WorldStem.java:18) 123at TRANSFORMER/minecraft@1.19/net.minecraft.server.Main.lambda$main$3(Main.java:158) 124at TRANSFORMER/minecraft@1.19/net.minecraft.Util.m_214652_(Util.java:768) 125at TRANSFORMER/minecraft@1.19/net.minecraft.Util.m_214679_(Util.java:763) 126at TRANSFORMER/minecraft@1.19/net.minecraft.server.Main.main(Main.java:157) 127at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 128at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) 129at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 130at java.base/java.lang.reflect.Method.invoke(Method.java:568) 131at MC-BOOTSTRAP/fmlloader@1.19-41.1.0/net.minecraftforge.fml.loading.targets.CommonServerLaunchHandler.lambda$launchService$0(CommonServerLaunchHandler.java:29) 132at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) 133at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) 134at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) 135at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.run(Launcher.java:106) 136at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.Launcher.main(Launcher.java:77) 137at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) 138at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.8/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) 139at cpw.mods.bootstraplauncher@1.1.2/cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141)
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.