Skip to content

FoxSamu/ModUtil

Repository files navigation

ModUtil

ModUtil is a Gradle plugin for Minecraft Forge mods, providing some small utilities.

Installation

In your gradle buildscript, at the very top, add this:

buildscript {
    repositories {
        // Include my maven repository
        maven { url "https://maven.shadew.net/" }
    }

    dependencies {
        // Include ModUtil libraries
        classpath "net.shadew.modutil:shade:1.0.2"
        classpath "net.shadew.modutil:inject-constants:1.0.2"
        classpath "net.shadew.modutil:changelog:1.0.2"
    }
}

// Apply plugins
apply plugin: "net.shadew.modutil.shade"
apply plugin: "net.shadew.modutil.inject-constants"
apply plugin: "net.shadew.modutil.changelog"

Even though Forge tells you not to edit the buildscript block, it's perfectly safe to add ModUtil as a dependency there, Forge will continue to work.

Usage

Constant injection (inject-constants)

ModUtil can inject constants into your Java code based on variables in the buildscript. To use constant injection, you first want to make some annotation somewhere in your mod's code that specifies a constant injection field. What about DynamicConstant:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface DynamicConstant {
    String value();
}

The annotation must have a value parameter: this parameter specifies the name of a constant you can define in your buildscript. To use this annotation as the constant injection annotation, specify it in the constants configuration block:

constants {
    annotation 'package.containing.your.DynamicConstant'
}

Now that you have configured your annotation, you can specify some constants in your java code:

@DynamicConstant("myconstant")
public static final String MYCONSTANT="typesomethingrandomhere";

Now there's one thing left: specifying your constant in the buildscript. You can specify constants in several ways, they all go into the constants block. The first way is to add a single constant:

constants {
    constant('myconstant', "I'm an injected constant! YAY!")
}

You can specify any value or variable as a constant. You can also specify multiple constants at once using a map:

constants {
    constants myconstant: "I'm an injected constant! YAY!", myotherconstant: "I'm yet another constant!"
}

You can also use a closure to obtain the constant at execution time. Internally, all constants are added to a list as functions that either look up the value of the constant for a given name, or return null if they couldn't look up the constant. By specifying a single constant, you implicitly add a function that returns the specified value if the specified key is passed to the function and null otherwise. The same way, you can add a map of constants that can be looked up from, the nature of Maps already is to return null for unknown keys. Using a closure, you can specify a very custom function. This funciont must get or compute the value for the constant given by name, or return null if that constant can't be resolved. You may choose to handle any value given here and not returning null at all.

constants {
    constants { name ->
        if (name == 'myconstant')
            return "I'm an injected constant! YAY!"
        return null
    }
}

Real-life example is to look up the constant value in a gradle.properties file:

constants {
    constants { name -> project.findProperty(name) }
}

However, this can be simplified to a single call to constantsFromProperties()

constants {
    constantsFromProperties()
}

Package shading

Package shading is the process of moving embedded dependencies in your jar file under a different package, so that you won't get conflicts with other mods having the same dependency embedded. Defining a package to be shaded is as simple as specifying it in the shading configuration block:

shading {
    shade('external.embedded.package', 'your.mod.package.shadedpackages.external.embedded.package')
}

This will move and refactor all classes in your jar file that are in the package external.embedded.package or any subpackage into the package your.mod.package.shadedpackages.external.embedded.package or any respective subpackage. The package shader will look for usages in your jar file (only in class files, not sources) and rename them to match the new package location.

Real-world example would be to include the PTG library into your mod's root package:

shading {
    shade('net.shadew.ptg', 'mymod.net.shadew.ptg')
}

Changelog generation

ModUtil provides tools for generating changelog files in two different formats: Markdown and Forge's version.json. The changelog generator takes a JSON file as input, and generates or updates any generated changelog file according to what's in that file.

To configure the changelog generator, first create a JSON file somewhere in your project. The JSON file has the following format:

{
  "version": {
    "number": "1.0",
    "name": "Nice Version Name",
    "minecraft": "1.16.3",
    "stable": true
  },
  "description": [
    "A description of your version.",
    "All strings here are joined by spaces.",
    "To add a newline, use: \n",
    "Markdown generator adds markdown newlines automatically"
  ],
  "changelog": [
    "Add changelog entries here",
    "Changelog entries don't appear in the version.json",
    "They only appear in the Markdown files",
    "The changelog is rendered as a bullet list",
    "Each changelog entry has a separate bullet in that list"
  ]
}

The version.number must be a valid version number according to the Semantic Version 2.0.0 specification, except that you may ignore the patch number if it's zero. The Minecraft version may be any string, the changelog generator does not bother about this.

To let ModUtil know about your changelog JSON, specify it in the changelog block:

changelog {
    changelogJson file("$rootDir/changelog.json")
}

Now that you have specified a changelog JSON file, you may specify some output files. The changelog generator can generate and update multiple files, whether they are of the same type or not. To specify a markdown output file, use markdownChangelog in your config block:

changelog {
    changelogJson file("$rootDir/changelog.json")
    markdownChangelog file("$rootDir/CHANGELOG_LATEST.md")
}

The Markdown file is deleted and re-created every time you update your versions. The above JSON configuration generates the following output:

## 1.0.0 - Nice Version Name

**For Minecraft 1.16.3**

A description of your version. All strings here are joined by spaces. To add a newline, use:   
Markdown generator adds markdown newlines automatically

#### Changelog

- Add changelog entries here
- Changelog entries don't appear in the version.json
- They only appear in the Markdown files
- The changelog is rendered as a bullet list
- Each changelog entry has a separate bullet in that list

You can also generate a version.json for Forge, which can be used in Forge's version checker. To specify a version.json output, use updateJson in your config block:

changelog {
    changelogJson file("$rootDir/changelog.json")
    markdownChangelog file("$rootDir/CHANGELOG_LATEST.md")
    updateJson file("$rootDir/version.json")
}

Unlike markdown files, this version.json file is parsed and updated every time you update your versions, and if parsed successfully, does not get overwritten but instead updated. The version.json will not contain the full changelog, only the description and version. The above changelog JSON generates the following (when no version.json was generated yet):

{
  "1.16.3": {
    "1.0": "1.0 - Nice Version Name: A description of your version. All strings here are joined by spaces. To add a newline, use: \n Markdown generator adds markdown newlines automatically"
  },
  "promos": {
    "1.16.3-latest": "1.0",
    "1.16.3-recommended": "1.0"
  }
}

Based on whether you have set the stable flag in your changelog JSON it updates the latest and recommended build. If your stable flag is set to true, it will update both recommended and latest versions to your new version. If it's set to false it will only update the latest version. When the version.json file already exists, it will not touch the versions already in there, it will only add new ones and update latest and recommended builds.

Now that your changelog generator is configured, you might want to acutally generate the files. This is done using the genChangelogs task

About

Gradle utilities for Forge and Fabric mods

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published