Skip to content

stianloader/sml6

Repository files navigation

SML6

While the SLL ecosystem is focused on transforming the game through runtime bytecode patches, the SML toolchain is focused on ahead of time recompilation of galimulator.

At this point, SML6 is technically game agnostic (provided it has), and has little focus on the decompilation and recompilation aspect it should have at a later point in time. Instead, the focus is initially set on the lengthy process of generating mapping files.

SML6 uses gslStarplane's Autodeobf configuration, meaning that it is also creating spStarmap-compatible mapping files.

Tasks

SML6 provides the following tasks:

  • org.stianloader.sml6.tasks.AggregateMappingsTask
  • org.stianloader.sml6.tasks.DeobfuscateGameTask
  • org.stianloader.sml6.tasks.FetchGameTask
  • org.stianloader.sml6.tasks.GenerateSourcesTask
  • org.stianloader.sml6.tasks.StripDependenciesTask
  • org.stianloader.sml6.tasks.XZTarBallerTask
  • org.stianloader.sml6.tasks.XZCompressTask

Unlike gslStarplane, all tasks should be compatible with gradle's configuration cache by default.

Please note that unlike gslStarplane, no tasks are configurated by default. As such, you need to register the tasks in the way you want. Of course, some conventions are applied for your sanity. Please refer to the "Task configuration" section for more in-depth information on the configuration of individual tasks.

SML6 also provides the following internal tasks; they are not meant for direct use by API users:

  • org.stianloader.sml6.tasks.AbstractArtifactTask

Task configuration

AggregateMappingsTask

The AggregateMappingsTask task defines following properties:

  • inputFormat (mandatory): Property<MappingFormat>, defines the format in which the input files are stored in.
  • outputFormat (mandatory): Property<MappingFormat>, defines the format of the output file.

The AggregateMappingsTask extends AbstractArchiveTask, meaning that the task inputs and outputs can be defined as it should be expected of tasks of that type.

The fully qualified name of MappingFormat is net.fabricmc.mappingio.format.MappingFormat which is an enum provided by fabric's mapping-io library. For convinience, sml6 provides the inputFormat(String) and outputFormat(String) methods to set the value of the respective properties without having to use the fully qualified name.

In essence AggregateMappingsTask is a task that converts mapping files between formats. However, the main usecase is converting an enigma directory into a single file - usually the tinyv2 or enigma formats. Although it might be theoretically possible to use this task to "explode" a mapping file into an enigma directory, doing so is not particularly recommended due to the semantics of AbstractArchiveTask.

This task does not perform descriptor inferrence or other things that might be required to convert from formats that might omit certain metadata.

Example task configuration:

task aggregateToTiny(type: org.stianloader.sml6.tasks.AggregateMappingsTask) {
    from "src/mappings"
    group = "build"
    archiveExtension = "tinyv2"

    inputFormat "enigma"
    outputFormat "tiny v2"
}

DeobfuscateGameTask

The DeobfuscateGameTask task defines following properties:

  • autoDeobfVersion: Property<String>, controlls the version of Autodeobf (only "5.0.2" supported at this point)
  • withAutodeobf: Property<Boolean>, controlls whether to run Autodeobf
  • withSLDeobf: Property<Boolean>, controlls whether to run slDeobf/oaktree
  • withSLDeobfMappings: Property<Boolean>, controlls whether to generate slIntermediary mappings
  • outputDirectory: DirectoryProperty, output files will be stored there by default
  • outputJar: RegularFileProperty, the deobfuscated jar will be stored there
  • inputJar (mandatory): RegularFileProperty, the obfuscated input jar
  • slIntermediaryMappings: RegularFileProperty, output file for the slIntermediary mappings generated by sl-deobf. Uses the tinyv1 format.
  • spStarmapMappings: RegularFileProperty, output file for the spStarmap mappings generated by Autodeobf. Uses the tinvyv1 format.

Unless otherwise specified, conventions exist that use sensible default values. For non-galimulator games, you may want to change withAutodeobf, withSLDeobf, and withSLDeobfMappings, though honestly only withAutodeobf is really mandatory. Keep in mind that SML6 expects games to always be a monojar, an assumption that really does not make much sense. You are welcome to change it (provided you ensure that it actually does what you want, I can fix my end later :p).

Note: Although this goes against common sense, the Switchmap classes get remapped via sl-deobf, but no mappings file will be generated. So … just don't touch them. This issue will be resolved eventually, but requires patches to sl-deobf.

FetchGameTask

The FetchGameTask task defines following properties:

  • aggressiveCaching: Property<Boolean>, whether to cache aggressively. This may easily result in outdated jars for games that update frequently, at the benefit of much better performance.
  • outputJar: RegularFileProperty, the resolved game jar will be stored at the location provided by this property.
  • primaryGameJar: RegularFileProperty, this is the primary location to look for the jar. If the jar does not exist at that location, only then it will try to find it through common steam installation directories. This property should be set to allow people to work with older versions of a game.
  • steamApplicationId (unused): Property<Integer>, the appId of the game to fetch. Currently unsused, but might reserved for future use.
  • steamApplicationName: Property<String>, the name of the steam application, or rather the name of the directory your game is located in relation to the "common" directory.
  • steamJarPath: Property<String>, the path of game jar in relation to the game's directory on steam.

The default values of this task are for galimulator. For other games, readjust the properties to follow your game.

SML6 expects games to be bundled as a single jar. However, many games are likely to be bundled across multiple jars. In that case, just create a merge request to SML6 to implement the desired functionality. I unfortunately lack the resources to do the job for you (after all, I won't be modding the same game as you).

GenerateSourcesTask

The GenerateSourcesTask task defines following properties:

  • decompileOptions: VFDecompileOptions, the options passed to the Vineflower decompiler
  • inputJar (mandatory): RegularFileProperty, the obfuscated input jar
  • javadocSources: ListProperty<MIOMappingsProvider>,
  • libraryClasspath: Property<@Nullable FileCollection>, a file collection that describes the library path forwarded to Vineflower. This file collection should only contain jars, other file types might break. Note: Default value is the resolved contents of the transitiveDependencies property.
  • lineRemappedOutputJar: RegularFileProperty, the file in which the line remapped jar should be stored. If VF was configured to remap LineNumberNodes, then this output file's line numbers will correspond to the decompiled source code. Otherwise, this is the input jar.
  • outputDirectory: DirectoryProperty, output files will be stored there by default
  • outputSourcesJar: RegularFileProperty, the decompiled output jar will be stored at this location.
  • transitiveDependencies: Provider<@Nullable Configuration>, a resolveable Configuration object that is the default source of the libraryClasspath property.

The GenerateSourcesTask has following subclass which is in turn used to configure the task further:

  • org.stianloader.sml6.tasks.GenerateSourcesTask.VFDecompileOptions

The GenerateSourcesTask decompiles the given input jar, and produces a sources jar for that jar, as well as a jar that contains line number information that corresponds to the sources jar (as well as possible that is, the tooling makes plenty of mistakes for now).

The library classpath passed to the decompiler mostly affects @Override annotations, though it may affect other small things such as casts when the type hierarchy isn't certain due to missing libraries or when generic signatures aren't known.

This task provides the following methods to more easily configure the task:

  • addJavadocSourcesDir(Action<MIOMappingsDirectoryProvider> configurationClosure): Register a MIOMappingsDirectoryProvider as a javadoc source.
  • addJavadocSourcesFile(Action<MIOMappingsFileProvider> configurationClosure): Register a MIOMappingsFileProvider as a javadoc source.
  • decompileOptions(Action<VFDecompileOptions> action): Configure the decompiler options as a closure
  • getDecompileOptions(): Get the VFDecompileOptions object used for configuration.

Example configuration:

task genSources(type: org.stianloader.sml6.tasks.GenerateSourcesTask, dependsOn: stripGalim) {
    inputJar = stripGalim.outputJar
    dependsOn tarballXZ
    transitiveDependencies = stripGalim.configuration

    decompileOptions {
        removeSynthetic = false
        verifyAnonymousClasses = true
        setOption("include-runtime", true)
    }

    addJavadocSourcesFile {
        containerFormat = TAR_XZ
        mappingFormat = ENIGMA
        mappingSource = project.file(tarballXZ.archiveFile.get().getAsFile())
        dstNamespaceId = -1 // Source namespace
    }
}

Most of the properties in the VFDecompileOptions class are automatically generated using Gradle's PropertyMixIn. The automatically generated property names are case-insensitive versions of the constant names within https://github.com/Vineflower/vineflower/blob/35f2c6e2b65746d8fc4358ced8de03d440f2b80b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java

StripDependenciesTask

The DeobfuscateGameTask task defines following properties:

  • configuration: Property<Configuration>, stores the configuration used for dependency resolution (note: consumed indirectly through strippingDependenciesFiles for fingerprinting reasons)
  • inputJar (mandatory): RegularFileProperty, the fat input jar
  • outputDirectory: DirectoryProperty, output files will be stored there by default
  • outputJar: RegularFileProperty, the stripped down jar will be stored there
  • strippingDependenciesFiles: Property<FileCollection>, defines a list of jars whoose contents should be removed from the input jar. By default, this will be the resolved configuration of this task.

To declare dependencies which should get stripped from the input jar, use the strip method. While of course you can still declare dependencies manually by going through the configuration property, this would be an unnecssarily convoluted approach.

Example configuration of this task:

task stripGalim(type: org.stianloader.sml6.tasks.StripDependenciesTask, dependsOn: deobfGalim) {
    inputJar = deobfGalim.outputJar

    strip "com.badlogicgames.gdx:gdx-backend-lwjgl:1.9.11"
    strip "com.badlogicgames.gdx:gdx-platform:1.9.11:natives-desktop"
    strip "com.badlogicgames.gdx:gdx-tools:1.9.11"
    strip "com.badlogicgames.gdxpay:gdx-pay-client:1.3.0"
    strip "com.code-disaster.steamworks4j:steamworks4j-server:1.8.0"
    strip "com.thoughtworks.xstream:xstream:1.4.7"
    strip "xpp3:xpp3:1.1.4c"
    strip 'junit:junit:4.13.2'

    // Or use
    // stripGalimulatorDefaults502()
    // to use sensible defaults when working with Galimulator 5.0.2 (input jar still needs to be defined through)
}

The dependency notation is the same as the notation you use within the gradle dependencies block.

XZTarBallerTask

The XZTarBallerTask class extends Tar.

The XZTarBallerTask task defines the following properties:

  • compressionLevel: Property<Integer>, the compression level to use for XZ. Maximum value is 9, however for small-ish files the default of 6 does the same thing.

All properties of Tar also apply, with the exception of compression, which is unused.

The archive's default destination directory is by default set to BasePluginExtension#getDistsDirectory. The default archive name is similarly derived. If the base plugin is absent, sane defaults will be used.

The XZTarBallerTask task is meant for shipping compressed mapping files. There is no real need for using this exact task, and other compression methods can be used. However, at the point of writing (2025-10-13), gslStarplane only supports .tar.xz and .xz compression. Though that may be subject to change in future versions of gslStarplane.

XZTarBallerTask is only meant to compress engima mapping directories. For other formats, use the XZCompressTask class instead.

Example task configuration:

task tarballXZ(type: org.stianloader.sml6.tasks.XZTarBallerTask) {
    from "src/mappings"
    group = "build"
    archiveExtension = "enigma.tar.xz"
}

assemble.dependsOn(tarballXZ)

XZCompressTask

The XZCompressTask class extends AbstractArtifactTask, and thus shares many of the properties present in AbstractArchiveTask, namely all the archive properties, as well as the destinationDirectory property.

The XZCompressTask task defines the following properties:

  • compressionLevel: Property<Integer>, the compression level to use for XZ. Maximum value is 9, however for small-ish files the default of 6 does the same thing.
  • inputFile: RegularFileProperty, the input file to compress

To improve ease-of-use (or to just alleviate the pains of muscle-memory), the XZCompressTask supports the from(Object) notation. However, keep in mind that the method may only be called once. Further, XZCompressTask can only compress a single file. To compress an entire directory or otherwise multiple files at once, consider using XZTarBallerTask instead. The into(Object) notation is not supported - not that it makes any sense in the first place.

The XZCompressTask task is meant for shipping compressed mapping files. There is no real need for using this exact task, and other compression methods can be used. However, at the point of writing (2025-Oct-13), gslStarplane only supports .tar.xz and .xz compression. Though that may be subject to change in future versions of gslStarplane.

The archive's default destination directory is set to the directory defined by BasePluginExtension#getDistsDirectory. The default archive name is similarly derived. If the base plugin is absent, sane defaults will be used.

To express the results of the XZCompressTask as a PublishArtifact, use the asArtifact() method provided by AbstractArtifactTask. You will need to mainly use this for maven publications.

Example task configuration:

task compressXZ(type: org.stianloader.sml6.tasks.XZCompressTask) {
    from  "tiny-file.tiny"
    group = "build"
    archiveExtension = "tiny.xz"
}

assemble.dependsOn(compressXZ)

publishing {
    publications {
        mavenJava(MavenPublication) {
            groupId = project.group
            artifactId = project.base.archivesName.get()
            version = project.version

            artifact compressXZ.asArtifact()
        }
    }
}

Task configuration objects

MIOMappingsProvider

Fully qualfiied name: org.stianloader.sml6.tasks.config.MIOMappingsProvider

The MIOMappingsProvider class defines following properties:

  • dstNamespaceId: Property<Integer> (optional, default: 0), the destination namespace id according to MIO. Should be equal to the namespace of the input jar.
  • srcNamespaceId: Property<Integer> (optional, default: -1), the source namespace id according to MIO. Not used in the javadocs process, but is used in the remapping process.
  • mappingFormat (mandatory): Property<net.fabricmc.mappingio.format.MappingFormat>, the used mapping format. Common values: ENIGMA_DIR, TINY_FILE, or TINY_2_FILE. Depends on the concrete input file though.

Instances of MIOMappingsProvider's subclasses need to be created through gradle's object factory (or just use helper methods). That being said, attempting to instantiate instances of this classes' subclasses is not exactly recommended. Creating instances of MIOMappingsProvider directly will fail, as for all intents and purposes it is an abstract class.

This class uses gradle's PropertyMixIn struct to make the MappingFormat and MappingContainer constants easily available as read-only constants. These properties are case-insensitive. Further, following aliases are applied:

  • enigma for ENIGMA_DIR
  • tiny for TINY_FILE
  • tiny2 for TINY_2_FILE
  • tinyv2 for TINY_2_FILE These aliases are also case-insensitive. Also please note that PropertyMixIn has little effects outside of closures, where you'd need to use the fully qualified path (or import statements but those are malpractice in stianloader buildscripts). Hence, one should prefer to configure things on demand using closures.

MIOMappingsDirectoryProvider

Fully qualfiied name: org.stianloader.sml6.tasks.config.MIOMappingsDirectoryProvider

This class extends MIOMappingsProvider, inheriting its properties.

The MIOMappingsDirectoryProvider class also defines following properties:

  • mappingSource (mandatory): DirectoryProperty (mandatory), the location of the mappings directory

Instances of this class need to be created through gradle's object factory.

MIOMappingsFileProvider

Fully qualfiied name: org.stianloader.sml6.tasks.config.MIOMappingsFileProvider

This class extends MIOMappingsProvider, inheriting its properties.

The MIOMappingsFileProvider class defines following properties:

  • mappingSource (mandatory): RegularFileProperty, the location of the mappings file to use
  • containerFormat (mandatory): Property<org.stianloader.sml6.starplane.remapping.MIOContainerFormat.MappingContainer>, the container format (either PLAIN, TAR_XZ, or XZ)

Keep in mind that MappingContainer constants are easily exposed through PropertyMixIn, see the documentation for MIOMappingsProvider.

Instances of this class need to be created through gradle's object factory.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages