Skip to content
This repository has been archived by the owner on May 16, 2024. It is now read-only.

For Developers

The Color Blurple edited this page Apr 28, 2024 · 2 revisions

Before we begin...

This wiki assumes you have at least a basic understanding of Java and other OOP/JVM languages.
It also assumes you at least know what Minecraft is, or more specifically Minecraft Modding.
Also note that this wiki may be out of date, maybe even significantly. Always check the newest updates for api changes.

Your First Mod

The mod created here is viewable in the examplemod directory.

First, import 1D6 using Jitpack and LibGDX from Sonatype. Below is an example using Gradle and Maven respectively.
Check this repo's gradle.properties for what versions to use.

repositories {
    mavenCentral()
    maven { url "https://jitpack.io" }
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
    maven { url "https://oss.sonatype.org/content/repositories/releases/" }
}
dependencies {
    // 1D6
    implementation "com.github.GirlInPurple:onedsix:d1.0.0" // Development 1.0.1
    implementation "com.github.GirlInPurple:onedsix:d1.0.1" // Development 1.0.1

    // LibGDX
    api "com.badlogicgames.gdx:gdx:$gdxVersion"
    api "com.badlogicgames.gdx:gdx-ai:$aiVersion" 
    // Technically you dont need the ones below this, but its worth it to get anyways.
    api "com.badlogicgames.ashley:ashley:$ashleyVersion"
    api "com.badlogicgames.box2dlights:box2dlights:$box2DLightsVersion"
    api "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    api "com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion"
    api "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
}
<!--
    Maven files are quite big, so this one doesn't have all the 
    imports as the Gradle one does due to space constraints.
-->
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
    <repository>
        <id>sonatype.snopshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
    <repository>
        <id>sonatype.releases</id>
        <url>https://oss.sonatype.org/content/repositories/releases/</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>com.github.GirlInPurple</groupId>
        <artifactId>onedsix</artifactId>
        <version>d1.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.badlogicgames.gdx</groupId>
        <artifactId>gdx</artifactId>
        <version>1.12.1</version>
    </dependency>
</dependencies>

First, lets create an Item and make it move the player X + 1 whenever its used outside battle.

import com.badlogic.gdx.math.Vector3;
import onedsix.Player;
import onedsix.gen.assets.*;

public class YourItem extends Item {
    
    // This means it will create an item with no texture, name, or model,
    // but will create everything else needed for the item to exist and be accessible.
    public YourItem(Attributes attributes, Recipe recipe, long roughCost) {
        super(attributes, recipe, roughCost);
    }
    
    // Whenever the item is used (outside battles) it will run this code here.
    @Override public void onUse(Player player) {
        player.position = new Vector3(
                player.position.x + 1,
                player.position.y,
                player.position.z
        );
    }
    
    // Your IDE will tell you to create these, you can ignore them for now
    @Override public void onInteract(Player player) {}
    @Override public void onInteractBattle(Player player) {}
    @Override public void onUseBattle(Player player) {}
}

Now, make a hook into ModStartupListener and make sure you register your item.

import example.YourItem;
import onedsix.event.modstartup.ModStartupEvent;
import onedsix.event.modstartup.ModStartupListener;
import onedsix.gen.DatagenHandler;
import org.slf4j.*;

public class ExampleMod implements ModStartupListener {
    
    private static final Logger L = LoggerFactory.getLogger(ExampleMod.class);
    
    @Override
    public void onStartup(ModStartupEvent event) {
        addCustomItem(YourItem.class);
    }
}

What if you wanted to depend on another mod? Here is where examplemod.mod.toml comes in.
Its based off NeoForge/Forge Minecraft mod loader's mods.toml, but here it has a special purpose.
The referralUrl option for dependencies allows for the game to automatically download mods off GitHub/GitLab/SourceForge.
From here, the game restarts and all the mods are applied/reapplied, depending on what they need.
Here is an example:

license="GPL-v3.0"
issueTrackerURL="https://github.com/You/ExampleMod/issues"

[[mods]]
  modId="examplemod"
  version="d1.0.0"
  displayName="Example Mod"
  repoURL="https://github.com/You/ExampleMod/"
  logoFile="logo.png"
  credits="someone, someone else, someone third"
  authors="You!"
  description='''
  This example is a modified version of the Forge wiki. Still compatible (mostly) tho.
  '''
  displayTest="MATCH_VERSION"

[[dependencies.examplemod]]
  modId="basemod"
  mandatory=true
  versionRange="[d1.0.0,)"
  ordering="NONE"
  side="BOTH"

[[dependencies.examplemod]]
  modId="onedsix"
  mandatory=true
  versionRange="[d1.0.0]"
  ordering="NONE"
  side="BOTH"

Now your mod's file layout should look like this:

.
|-- src/
|   `-- main/
|       |-- java/
|       |   `-- example/
|       |       |-- ExampleMod.java
|       |       `-- YourItem.java
|       `-- resources/
|           `-- examplemod.mod.toml
`-- build.gradle

And you're done! ...for the most part.
The mod will build successfully and add an item to the game, but the item uses error.png as its texture and model, and thats not great. It doesnt even have a name!
Thats where you come in, thats your job to figure out. Have fun!

So... about ASM?

ASM is a framework for modifying Java Bytecode at runtime, and many projects use it, like SpongePowered/Mixin, ByteBuddy, and many others.
1D6 has a hook for "safely" modifying game code before launch, the AsmPhase interface.

Compiled alongside 1D6 is ByteBuddy 1.14.14 and ASM 9.6.
Implementing AsmPhase somewhere in your mod give you the ability to modify anything you like, within reason of course.
For this reason, one of the guidelines of contributing the 1D6 is to never use Enums, as they are (near) impossible to modify, even with ASM.
You can even modify other mods with it as well, just be careful as ASM crashes aren't handled too well...

Below is a small example of what ByteBuddy can do.
Here it is creating a new class extending Item,
then overriding toString() with a set value,
and making a new method (tester()) that returns "this is a test",
and finally creates it and prints out some values.

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import onedsix.event.asmphase.AsmPhaseEvent;
import onedsix.event.asmphase.AsmPhaseListener;
import onedsix.gen.assets.Attributes;
import onedsix.gen.assets.Item;
import onedsix.gen.assets.Recipe;

public class AsmHandler implements AsmPhaseListener {
    
    @Override
    public void onAsm() {
    
        Class<? extends Item> dynamicType = new ByteBuddy()
            .subclass(Item.class)

            .method(ElementMatchers.named("toString")).intercept(FixedValue.value("Hello World!"))
            .defineMethod("tester", String.class, Visibility.PUBLIC).intercept(FixedValue.value("this is a test"))

            .make().load(AsmHandler.class.getClassLoader()).getLoaded();
    
        Item instance = dynamicType.getConstructor(Item.class).newInstance(
                new Attributes(),
                new Recipe(null, null, null, null),
                0L
        );
        
    }
}

All I ask of you is to be very careful what you do with this power. ASM breaks stuff, alot.

More Advanced APIS

TODO

Clone this wiki locally