

Minecraft modding for competent Java programmers · GitHub
source link: https://gist.github.com/HybridEidolon/fe467dab9eb885981ddd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Minecraft Forge New Mod Guide
Make sure you have Gradle installed and in your path, so you can run it from command line. Otherwise, you should copy a bootstrapper and the gradle jar into your project.
Use this template build.gradle (for the MC 1.8 unstable branch) for your gradle script:
buildscript {
repositories {
mavenCentral()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
}
}
apply plugin: 'forge'
version = "0.1.0"
group = "com.mydomain.mymod"
archivesBaseName = "mymod"
minecraft {
*version = "1.8-11.14.0.1274-1.8"*
runDir = "eclipse"
*mappings = "snapshot_nodoc_20141130"*
}
/*
processResources {
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
// substitute values in mcmod.info
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
expand 'version':project.version, 'mcversion':project.minecraft.version
}
from(sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
}
}
*/
Save this in a new directory and run gradle setupDecompWorkspace
and gradle build
to set everything up on your local cache. Gradle will be smart and not
have multiple copies of this cache if you are working on multiple mods with
the same target MC version.
Replace version
and mappings
in the minecraft
block to change the MCF
version your mod targets, as well as the obfuscation mappings from MCP to use.
You do not need mappings
if you are using a stable build.
You can uncomment the processResources
block and extend it as necessary to
provide build-time substitution to your mcmod.info
file (which should go in
src/main/resources). A template for mcmod.info
is as follows:
[
{
"modid": "mymod",
"name": "My Mod",
"description": "A mod that does things.",
"version": "${version}",
"mcversion": "${mcversion}",
"url": "http://mydomain.com",
"updateUrl": "",
"authorList": ["Me", "Another person"],
"credits": "Forge, FML, and MCP, for being incredible",
"logoFile": "",
"screenshots": [],
"dependencies": []
}
]
And a hello world template MyModMain.java
for your mod class:
package com.mydomain.mymod;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
@Mod(modid="mymod", name="My Mod", version="0.1.0")
public class MyModMain {
@EventHandler
public void onInit(FMLInitializationEvent event) {
System.out.println("Hello, world!");
}
}
You can generate IDE project files with gradlew idea
for IntelliJ and
gradlew eclipse
for Eclipse.
Useful gradle targets:
runClient
- runs the client using the "eclipse" folder as CWDrunServer
- similar to the above but for the serverbuild
- total build, drops jar inbuild/libs/
clean
- cleans the build directory (doesn't remove IDE project files!)
Coding Details
This is a coding tutorial for experienced Java developers. It is not a fully fledged Forge tutorial. You are expected to understand the following concepts:
- Classes and Interfaces
- Class Inheritance
- Annotations
- Reflection
- Event-handling as a design concept
- Reading javadocs and source code tirelessly scrounging up information
Declaring your mod
To declare your mod to ForgeModLoader, create a class (doesn't have to be
anything specific, as long as it is instantiable i.e. non abstract) and give it
the @Mod
class annotation.
@Mod(modid="mymodid", name="My Mod")
public class MyMod {
}
Hooking events
In Forge, the preferred way of adding functionality to Minecraft is to hook
events and register data classes. Let's start with a simple hook. For
ForgeModLoader events (FML), they can simply be dropped into the same class that
is marked @Mod. We use the method annotation @EventHandler
for this.
@Mod(modid="mymodid", name="My Mod")
public class MyMod {
@EventHandler
public void onModInitialized(FMLInitializationEvent event) {
System.out.println("Hello, world!");
}
}
On startup of minecraft or minecraft-server, the log will print this message now.
The only thing that matters in an event registration is the argument. FML will
infer the handler on its own using the argument list. In this case, the method
onModInitialized
will only be called when FMLInitializationEvent
is fired.
See the documentation for net.minecraftforge.fml.common.Mod.EventHandler
for
details on the different kinds of FML events.
The other kind of events are global to Forge, and pertain directly to Minecraft functionality. To use them, we use a different annotation.
@Mod(modid="mymodid", name="My Mod")
public class MyMod {
@EventHandler
public void onModInitialized(FMLInitializationEvent event) {
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
System.out.println("A world was loaded. The world difficulty is: " +
I18n.format(event.world.getDifficulty().getDifficultyResourceKey()););
}
}
When a world loads, the world difficulty will be printed to standard out.
See the documentation for the net.minecraftforge.event
package for a list of
all the events in Forge.
Proxy classes: separating Client and Server Code
In the dedicated server, client code is not available, and attempting to
reference client classes will crash the server. To avoid this, you can use the
@SidedProxy
annotation on a static field in any class.
Here's an example of how to set up a proxy:
// -- CommonProxy.java
package com.myname.mymodid;
public class CommonProxy {
public void doThings() {
// ... this function behaves differently if it's on minecraft-server
System.out.println("This is common code for all environments.");
return;
}
}
// -- CombinedClientProxy.java
package com.myname.mymodid;
public class CombinedClientProxy extends CommonProxy {
public void doThings() {
super.doThings(); // common code print
System.out.println("This is the user client! Maybe force load a post-process shader here?");
}
}
// -- MyMod.java
package com.myname.mymodid;
@Mod(modid="mymodid", name="My Mod")
public class MyMod {
@SidedProxy(clientSide="com.myname.mymodid.CombinedClientProxy",serverSide="com.myname.mymodid.CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void onModInitialized(FMLInitializationEvent event) {
MinecraftForge.EVENT_BUS.register(this);
proxy.doThings();
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
System.out.println("A world was loaded. The world difficulty is: " +
I18n.format(event.world.getDifficulty().getDifficultyResourceKey()););
}
}
On a client (minecraft.jar) this will call both CommonProxy
and
CombinedClientProxy
's versions of the function.
Do not use @SidedProxy
for network related behavior, however, there is a
different way to do that.
Doing things on the Minecraft main thread.
AKA Here's how you do reflection as well so that it works even after reobfuscation, fyi. THIS IS REALLY CRITICAL FOR DISTRIBUTING YOUR MOD
TODO: DO THIS RIGHT! THE ABOVE IS WRONG
Some functions need to be called on the Minecraft thread (in particular, anything related to rendering). Thankfully Forge lets us queue functions to be called on the main thread. This is useful when we want to call those functions inside event handlers. Here's an example:
package com.myname.mymodid;
@Mod(modid="mymodid", name="My Mod")
public class MyMod {
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
// Force FXAA
// We have to queue this to run on the minecraft thread because it needs
// to refer to the GL context
final Minecraft mc = Minecraft.getMinecraft();
I18n.format(event.world.getDifficulty().getDifficultyResourceKey());
mc.addScheduledTask(new Runnable() {
public void run() {
if (mc.entityRenderer.getShaderGroup() != null)
mc.entityRenderer.getShaderGroup().deleteShaderGroup();
Method method;
try {
method = EntityRenderer.class.getDeclaredMethod("loadShader", ResourceLocation.class); // loadShader
method.setAccessible(true);
method.invoke(mc.entityRenderer, new ResourceLocation("shaders/post/fxaa.json"));
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
}
}
Providing a Runnable
or Callable
will queue that code up for execution or
immediately run it if we are already on the Minecraft thread.
Registering new items/achievements/blocks
Handling networking
Sending arbitrary packets
Class for custom payloads is Packet250CustomPayload
. Send them with:
PacketDispatcher.sendPackettoServer()
PacketDispatcher.sendPacketToServer()
PacketDispatcher.sendPacketToPlayer()
PacketDispatcher.sendPacketToAllAround()
PacketDispatcher.sendPacketToAllInDimension()
PacketDispatcher.sendPacketToAllPlayers()
Minecraft Shader Documentation
Shaders in Minecraft are handled through materials described in json objects. At this time, shaders can only be applied with post-process passes.
post-process descriptors go in:
shaders/post/*.json
material descriptors in:
shaders/program/*.{fsh,vsh}
shaders/program/*.json
Post-process descriptors
These describe how each pass is applied to the main framebuffer. A simple, no-frills post-process (i.e. no post-processing at all) might look like this:
{
"targets": [ "swap" ],
"passes": [
{
"name": "blit",
"intarget": "minecraft:main",
"outtarget": "minecraft:main"
},
],
}
This post-process description uses the material program blit
to draw to outtarget
minecraft:main
, which is in the minecraft jar but as far as I can tell, you would have to copy this into your mod from the official assets to use it because the descriptors don't support resource locators with mod contexts. blit
simply takes the intarget
and blits it to the outtarget
, nothing more or less. (You will see how this works in the example for material descriptors below.)
Materials
Materials are a set of properties for drawing a surface. They include GL blend function settings, the program code to use, and declarations for samplers and uniforms. The calling code that uses the material needs to set these or the render might fail.
An example one from minecraft:shaders/program/blit.json
:
{
"blend": {
"func": "add",
"srcrgb": "srcalpha",
"dstrgb": "1-srcalpha"
},
"vertex": "blit",
"fragment": "blit",
"attributes": [ "Position" ],
"samplers": [
{ "name": "DiffuseSampler" }
],
"uniforms": [
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] },
{ "name": "ColorModulate", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
]
}
As far as I can tell, DiffuseSampler
is set to the intarget
framebuffer in the post-process handling code.
Post-process in Practice
The "Super Secret Settings" simply cycles through a list of predefined post-process descriptors. Looking at this code, the process for changing the current post-process shader is as so:
// We have to queue this to run on the minecraft thread because it needs
// to refer to the GL context
final Minecraft mc = Minecraft.getMinecraft();
mc.addScheduledTask(new Runnable() {
public void run() {
if (mc.entityRenderer.getShaderGroup() != null)
mc.entityRenderer.getShaderGroup().deleteShaderGroup();
Method method;
try {
method = EntityRenderer.class.getDeclaredMethod("loadShader", ResourceLocation.class); // loadShader
method.setAccessible(true);
method.invoke(mc.entityRenderer, new ResourceLocation("shaders/post/fxaa.json"));
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
HOWEVER, loadShader(ResourceLocation)
is currently private access which means you have to either reflect it using Java Reflection or use the Access Transformers API in Forge to force it to public access.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Recommend
-
37
Filed under: The Nokia 7.2 is competent and almost as good as the Pixel 3A Google raised the...
-
20
Rust for Modding Smash Ultimate I'm gonna be upfront with you here, I'm one of those people obsessed with Rust. This is gonna be a post ripe with bi...
-
5
IdeasAmerica’s Next Authoritarian Will Be Much More CompetentTrump was ineffective and easily beaten. A future strongman won’t be.November 7, 2020
-
10
Computer scientist does not imply competent sysadmin There are some people who have no business being sysadmins. For whatever reason, they are missing the kind of systems knowledge which it takes to be useful. Unfortunately, far...
-
7
README.md Fabric API Essential hooks for modding with Fabric. Fabric API is the library for essential hooks and interoperability mechanisms for Fabr...
-
8
What up friends! 👋 Hope you're doing well and safe out there! This is the introduction of a series called "Journey into Minecraft Modding". Following my journey creating a mod for Minecraft called
-
425
D2RModding Modding for Diablo 2 Resurrected This is for single player only and not intented to affect the gameplay of others. It currently allows the use of all classes and TCP/IP for LAN'ing with those who have also pu...
-
14
NodeJS developer at Competent Groove (2+ Years Exp)At Competent Groove, we are the team of...
-
10
Frontend (Angular) developer at Competent Groove (1 to 2+ Years Exp)At Competent Groove, we...
-
8
NFTWIIZ is one of the leading NFT Marketplace Development Services offering customized NFT platforms based on your needs. Get a free demo! Take To Our Expert>> 👉 Email: [email protected] 👉 Skype: https://join.skype.com/in...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK