package org.stianloader.sll; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Properties; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.ApiStatus.AvailableSince; import org.json.JSONObject; import org.slf4j.LoggerFactory; import de.geolykt.starloader.util.JavaInterop; /** * Obtain metadata information about this SLL (stianloader-launcher) environment. * *
This mainly focuses on traits about the environment that might differ greatly between installations. * Though in truth the chance of there being significant difference is low as users will in general not * use their own SLL install but use whatever installation is already provided for them (i.e. through modpacks * or by the game itself). * *
Thus the traits are largely focused on the Mixin capabilities of SLL, alongside vendor information. * * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") public class SLLEnvironment { @NotNull private static final String MIXIN_COMPILED_ARTIFACT_ID; @NotNull private static final String MIXIN_COMPILED_GROUP_ID; @NotNull private static final String MIXIN_COMPILED_VERSION; private static final boolean MIXIN_SPONGELIKE; @NotNull private static final String SLL_ARTIFACT_ID; @NotNull private static final String SLL_GROUP_ID; @NotNull private static final String SLL_VERSION; static { String sllGroup = null; String sllArtifact = null; String sllVersion = null; String mixinGroup = null; String mixinArtifact = null; String mixinVersion = null; String mixinSpongelike = null; try (InputStream is = SLLEnvironment.class.getResourceAsStream("/sll-environment-meta.json")) { if (is == null) { throw new IOException("Resource not found."); } JSONObject jsonObject = new JSONObject(new String(JavaInterop.readAllBytes(is), StandardCharsets.UTF_8)); sllGroup = jsonObject.optString("sll_groupid", null); sllArtifact = jsonObject.optString("sll_artifactid", null); mixinGroup = jsonObject.optString("mixin_groupid", null); mixinArtifact = jsonObject.optString("mixin_artifact", null); mixinVersion = jsonObject.optString("mixin_version", null); mixinSpongelike = jsonObject.optString("mixin_spongelike", null); // Nightly-paperpusher alters the version of the pom.properties file generated by maven. This is how we // can fetch the current version of SLL. readPomMeta: try (InputStream is2 = SLLEnvironment.class.getResourceAsStream("/META-INF/maven/" + sllGroup + "/" + sllArtifact + "/pom.properties")) { if (is2 == null) { sllVersion = jsonObject.optString("sll_version_fallback", "unknown"); break readPomMeta; } Properties properties = new Properties(); properties.load(is); String reportedVersion = properties.getProperty("version"); sllVersion = reportedVersion == null ? jsonObject.optString("sll_version_fallback", "unknown") : reportedVersion; } catch (IOException ignored) { } } catch (IOException e) { LoggerFactory.getLogger(SLLEnvironment.class).warn("Failed to fetch environment metadata.", e); } if (mixinSpongelike == null) { try { Class.forName("org.spongepowered.asm.mixin.Mixins", false, SLLEnvironment.class.getClassLoader()); mixinSpongelike = "true"; } catch (ClassNotFoundException cnfe) { mixinSpongelike = "false"; } } MIXIN_COMPILED_ARTIFACT_ID = mixinArtifact == null ? "unknown" : mixinArtifact; MIXIN_COMPILED_GROUP_ID = mixinGroup == null ? "unknown" : mixinGroup; MIXIN_COMPILED_VERSION = mixinVersion == null ? "unknown" : mixinVersion; MIXIN_SPONGELIKE = Boolean.parseBoolean(mixinSpongelike); SLL_GROUP_ID = sllGroup == null ? "unknown" : sllGroup; SLL_ARTIFACT_ID = sllArtifact == null ? "unknown" : sllArtifact; SLL_VERSION = sllVersion == null ? "unknown" : sllVersion; } /** * Obtains the artifactId of the mixin implementation this version of SLL * was compiled against. This gives a good idea about what the runtime * mixin implementation is based on. In practice it doesn't mean much though, * but is useful information for the developer, especially as it highlights the * capabilities of the mixin implementation. * *
Please note that the artifactId alone does not suffice to completely * distinguish mixin implementations. For example, the stianloader organisation * as well as fabric both offer "sponge-mixin" under the "org.stianloader" and * "net.fabricmc" groupIds respectively. Hence, artifactIds and groupIds * are tightly linked together. * *
Also note that it is generally possible in the java ecosystem to swap out * singular classes or even entire packages or jars. As such, it is possible (albeit * unlikely outside of a modular environment) that the reported data does not match * the actual runtime data. * * @return The groupId of the mixin implementation the SLL environment * was built against. * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") @NotNull @Contract(pure = true) public static String getMixinCompiledArtifactId() { return SLLEnvironment.MIXIN_COMPILED_ARTIFACT_ID; } /** * Obtains the groupId of the mixin implementation. This gives a * good idea on who the author of the mixin implementation is, * especially useful for assigning blame. In practice it only * hints at who is vendoring the mixin implementation binary. * *
Please note that the groupId alone does not suffice to distinguish * between mixin implementations. For example, the stianloader organisation * offers both "micromixin-transformer" and "sponge-mixin" under the * "org.stianloader" groupId. * *
Also note that it is generally possible in the java ecosystem to swap out * singular classes or even entire packages or jars. As such, it is possible (albeit * unlikely outside of a modular environment) that the reported data does not match * the actual runtime data. * * @return The groupId of the mixin implementation the SLL environment * was built against. * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") @NotNull @Contract(pure = true) public static String getMixinCompiledGroupId() { return SLLEnvironment.MIXIN_COMPILED_GROUP_ID; } /** * Obtains the version of the mixin implementation this SLL binary was compiled * against. Note that it is generally possible in the java ecosystem to swap out * singular classes or even entire packages or jars. As such, it is possible (albeit * unlikely outside of a modular environment) that the reported data does not match * the actual runtime data. * *
Mixin implementation versions can be extremely chaotic. Sponge's Mixins and * micromixin-transformer, as well as other reimplementations will more likely than not * stay with a "sane" X.Y.Z release format. However, the version naming scheme of * forks will be a bit chaotic by nature. For example, stianloader's mixin fork * (but not micromixin-transformer) has version names such as "0.16.4-0.8.7-1-a20250906". * * @return The version string of the mixin implementation binary * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") @NotNull @Contract(pure = true) public static String getMixinCompiledVersion() { return SLLEnvironment.MIXIN_COMPILED_VERSION; } /** * Obtains the artifactId of the SLL environment. * *
This gives a good hint as to what the SLL binary is being vendored as. * It also gives a hint to the intended mixin environment. For example, the * launcher binary using micromixin has the artifactId of launcher-micromixin. * However finding out information about the mixin environment through that way * is prone to failure in the future. * * @return SLL's artifact groupId. * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") @NotNull @Contract(pure = true) public static String getSLLArtifactId() { return SLLEnvironment.SLL_ARTIFACT_ID; } /** * Obtains the groupId of the SLL environment. This gives a good hint at who the vendor of * the compiled SLL binary is. * * @return SLL's artifact groupId. * @since 4.0.0-a20250922 */ @AvailableSince("4.0.0-a20250922") @NotNull @Contract(pure = true) public static String getSLLGroupId() { return SLLEnvironment.SLL_GROUP_ID; } /** * Obtains the the version string of the SLL binary used in this environment. * *
Please note that the returned string is generally not semver-compliant.
* Instead, should be interpreted as a maven version string. Tools to compare
* such versions are provided by picoresolve (which SLL already depends on),
* though other libraries exist for the same result.
*
* @return The version string.
* @since 4.0.0-a20250922
*/
@AvailableSince("4.0.0-a20250922")
@NotNull
@Contract(pure = true)
public static String getSLLVersion() {
return SLLEnvironment.SLL_VERSION;
}
/**
* Query whether the used Mixin implementation is similar to
* sponge's original Mixin implementation. In other words,
* this method returns true if the mixin implementation
* is Mixin or a fork of Mixin, whilst it returns false
* if the implementation happens to be a rewrite of the mixin
* API that may or may not have been written from scratch.
*
* @return Whether the mixin implementation is "sponge-like",
* using the definitions defined above.
* @since 4.0.0-a20250922
*/
@AvailableSince("4.0.0-a20250922")
@Contract(pure = true)
public static boolean isMixinSpongelike() {
return SLLEnvironment.MIXIN_SPONGELIKE;
}
}