This project intends to be a command-line tool to apply configurable transformations to Java source-code without user-interaction. It is intended to be integrated into the NeoGradle pipeline for producing a Minecraft Jar-File for use in modding projects.
The source code is parsed using IntelliJ Community Edition libraries.
Inserts additional class, method, and field Javadoc elements. The majority of these are sourced from Parchment.
During deobfuscation, the method parameter names are set to stable but automatically generated names. To aid developers in understanding the decompiled code, and for better code generation when overriding decompiled methods in mods, method parameter names are replaced with crowdsourced information from Parchment.
JST supports injecting interfaces to transformed classes in a data-driven fashion, and creates empty stubs for the classes
to be able to still compile the modified code without access to the actual interface definitions.
This feature allows interfaces added at runtime using Mixins, Coremods, or other transformation mechanics to be visible at compile-time.
The format of the interface injection data file is quite straightforward:
{
"targetClassBinary": ["interfaceBinary"] // Can be a single interface, or an array of interfaces to implement
}
Where:
targetClassBinary
is the binary representation of the target class to inject the interfaces to (e.g.net/minecraft/world/item/Item
, ornet/minecraft/world/item/Item$Properties
)interfaceBinary
is the binary representation of an interface to inject (e.g.com/example/examplemod/MyInjectedInterface
)
The interfaces can have generic parameter declarations. Assuming the target class EntityType
has a T
generic parameter,
we could implement Supplier<T>
using java/util/function/Supplier<T>
.
Note
Generics are copied verbatim. If you need the generics to reference a class, please use its fully qualified name (e.g. java/util/function/Supplier<java.util.concurrent.atomic.AtomicInteger>
).
Third parties can use JST to implement their source file own transformations.
To do so, you can depend on the net.neoforged.jst:jst-api
artifact, and implement the net.neoforged.jst.api.SourceTransformerPlugin
service:
- the
getName
method returns the unique CLI identifier of the transformer. It will generate--[no]-enable-{name}
CLI options - the
createTransformer
method creates aSourceTransformer
that will handle the replacements. The transformer will also be given to picocli to intercept custom CLI arguments
To create the executable jar with your custom transformer, you should shadow the net.neoforged.jst:jst-cli
artifact and its dependencies, and set the main class to net.neoforged.jst.cli.Main
.
Note that this tool is not intended to be run by users directly. Rather it is integrated into the NeoGradle build process.
It can be invoked as a standalone executable Jar-File. Java 17 is required.
Usage: jst [-hV] [--in-format=<inputFormat>] [--libraries-list=<librariesList>]
[--max-queue-depth=<maxQueueDepth>] [--out-format=<outputFormat>]
[--classpath=<addToClasspath>]... [--ignore-prefix=<ignoredPrefixes>]...
[--enable-parchment --parchment-mappings=<mappingsPath> [--[no-]parchment-javadoc]
[--parchment-conflict-prefix=<conflictPrefix>]] [--enable-accesstransformers
--access-transformer=<atFiles> [--access-transformer=<atFiles>]...
[--access-transformer-validation=<validation>]] [--enable-interface-injection
[--interface-injection-stubs=<stubOut>]
[--interface-injection-marker=<annotationMarker>]
[--interface-injection-data=<paths>]...] INPUT OUTPUT
INPUT Path to a single Java-file, a source-archive or a folder containing the
source to transform.
OUTPUT Path to where the resulting source should be placed.
--classpath=<addToClasspath>
Additional classpath entries to use. Is combined with --libraries-list.
-h, --help Show this help message and exit.
--ignore-prefix=<ignoredPrefixes>
Do not apply transformations to paths that start with any of these
prefixes.
--in-format=<inputFormat>
Specify the format of INPUT explicitly. AUTO (the default) performs
auto-detection. Other options are SINGLE_FILE for Java files, ARCHIVE
for source jars or zips, and FOLDER for folders containing Java code.
--libraries-list=<librariesList>
Specifies a file that contains a path to an archive or directory to add
to the classpath on each line.
--max-queue-depth=<maxQueueDepth>
When both input and output support ordering (archives), the transformer
will try to maintain that order. To still process items in parallel, a
queue is used. Larger queue depths lead to higher memory usage.
--out-format=<outputFormat>
Specify the format of OUTPUT explicitly. Allows the same options as
--in-format.
-V, --version Print version information and exit.
Plugin - parchment
--enable-parchment Enable parchment
--parchment-conflict-prefix=<conflictPrefix>
Apply the prefix specified if a Parchment parameter name conflicts with
existing variable names
--[no-]parchment-javadoc
Whether Parchment javadocs should be applied
--parchment-mappings=<mappingsPath>
The location of the Parchment mappings file
Plugin - accesstransformers
--access-transformer=<atFiles>
--access-transformer-validation=<validation>
The level of validation to use for ats
--enable-accesstransformers
Enable accesstransformers
Plugin - interface-injection
--enable-interface-injection
Enable interface-injection
--interface-injection-data=<paths>
The paths to read interface injection JSON files from
--interface-injection-marker=<annotationMarker>
The name (binary representation) of an annotation to use as a marker for
injected interfaces
--interface-injection-stubs=<stubOut>
The path to a zip to save interface stubs in
The source code in this repository is licensed under the LGPL 2.1.
Most of the heavy lifting is done by third party libraries such as the IntelliJ platform or Mapping IO, which are under different licenses. Please refer to these projects and keep in mind that the standalone executable tool will contain code from these projects.